Apply the 32-bit CPU list on Proton builds without _NET_WM_WOW64

Merged
UT
14h ago
+95-6

Description

The 32-bit CPU list ("Processor affinity (32-bit)", cpuListWoW64) never reached 32-bit games in X11 sessions on Proton 10 and later. Seen on a Retroid Pocket 6 with Borderlands 2 (Proton 10 arm64ec, FEX through libwow64fex): cpuList 0-7, cpuListWoW64 4-7, and every Borderlands2.exe thread showed Cpus_allowed_list: 0-7.

How the lists are applied today

  • The masks are built once in XServerScreen from getCPUList(true) / getCPUListWoW64(true) and handed to Win32AppWorkarounds.
  • X11 session: when a top-level application window maps, Win32AppWorkarounds.applyWindowWorkarounds picks the WoW64 mask if the window's _NET_WM_WOW64 X property is 1, else the main mask, and sends it with the window's _NET_WM_PID to winhandler.exe (SET_PROCESS_AFFINITY). winhandler calls SetProcessAffinityMask; wineserver sets every thread of the process with sched_setaffinity, and new threads and child processes take the process mask from wineserver, so threads created later are covered.
  • Gamescope session: ContainerAffinity waits for the first game content, lists processes through winhandler (which reports WoW64 from IsWow64Process) and applies the mask per process.
  • steam.exe is skipped, and so is the game when power control has pinned it.

Root cause
_NET_WM_WOW64 comes from a Winlator winex11 patch. Proton 10 and later do not have it: the device's proton-10.0-arm64ec/lib/wine/aarch64-unix/winex11.so contains _NET_WM_PID but not _NET_WM_WOW64. So Window.isWoW64() is always false, and every 32-bit game window gets the main list. On the RP6 that list is 0-7, which matches what we saw. 64-bit games are not affected because they use the main list anyway (the Odin 3 64-bit game got 0-5 as set).

Fix
If a mapped window has no _NET_WM_WOW64 property at all, Win32AppWorkarounds gives it to ContainerAffinity.applyToWindowProcess:

  • If cpuListWoW64 is still the container default (the upper half of the cores), the process gets the main list, exactly as today. No process list is fetched.
  • If cpuListWoW64 differs from the default, meaning someone set it explicitly, a background thread takes the winhandler process list, looks up the window's PID and sends the 32-bit list when winhandler reports the process as WoW64. If the process is missing from the list or winhandler does not answer, it sends the main list.

Wine builds that do set the property work as before. The steam.exe and power-control skips still run first. Nothing changes for gamescope sessions or for 64-bit processes.

Unit tests in ContainerAffinityTest: default 32-bit list -> main mask; explicit 32-bit list -> 32-bit mask; 64-bit or unlisted process -> main mask; empty mask -> no request.

Behaviour: this does not move 32-bit games to a smaller core set unless someone asked for it. On Proton 10+ in X11 sessions, a 32-bit game whose cpuListWoW64 is the untouched default keeps getting the main CPU list, as it does today. Only an explicitly set 32-bit list changes where it runs.

Recording

None. To verify on device, while a 32-bit game is in gameplay: for t in /proc/<game pid>/task/*; do grep Cpus_allowed_list $t/status; done shows the 32-bit list on every thread, and logcat shows ContainerAffinity: Applied mask ... wow64=true.

Type of Change

  • Bug fix
  • Performance / stability improvement
  • Compatibility improvements
  • Other (requires prior approval)

Checklist

  • If I have access to #code-changes, I have discussed this change there and it has been green-lighted. If I do not have access, I have still provided clear context in this PR. If I skip both, I accept that this change may face delays in review, may not be reviewed at all, or may be closed.
  • This change aligns with the current project scope (core functionality, stability, or performance). If not, it has been explicitly approved beforehand.
  • I have attached a recording of the change.
  • I have read and agree to the contribution guidelines in CONTRIBUTING.md.

Summary by CodeRabbit

  • Bug Fixes
    • Improved CPU affinity handling for application windows, including compatibility with WoW64 processes when window metadata is unavailable.
    • Applications are left unchanged when no matching process is found or no applicable CPU affinity is configured.
    • When process details are available, the configured WoW64 affinity is applied only to WoW64 processes; other processes use the main affinity setting.