Wine numbers the Windows CPUs like the host, so Windows CPU 0 was host
core 0, a small core on most Android chips. Games that pin their main
thread to CPU 0 ran it there: GTA IV 32 fps on the AYANEO Pocket FIT,
61 fps once CPU 0 is the fastest core.
droiddeck-game-env hands Wine the game's cores fastest first, from each
core's cpu_capacity (or its top clock). Equal cores keep their order.
Cores picked globally or in a game profile stay the only cores the game
sees, now fastest first; a pick only counts when it narrows the cores.
Games on Proton's own default_cpu_limit list keep their count on the
fastest cores (quoted and unquoted counts); other values such as "P"
are left to Proton. A typed WINE_CPU_TOPOLOGY / PROTON_CPU_TOPOLOGY
still wins, an empty one turns it off for a game, and
DROIDDECK_FASTEST_CORES=0 turns it off. Steam's own Proton runs in
compatdata/0 are left as before.
Builds on maxjivi05's "Windows games see only the cores they are pinned
to" (9d85af75).
* feat(launcher): apps page with search, sort, filter, folders, widgets and page swipes
* feat(launcher): app shortcuts, pinned shortcuts, hidden apps and LauncherApps listing
* fix(ui): let page content scroll to the bottom edge
* feat(launcher): accept widgets other apps ask to pin
* i18n(launcher): translate apps page strings
* fix(launcher): keep disabled pins, serialize widget adds, add many plurals
* fix(launcher): drop in-flight pin refreshes after unpinning
* fix(launcher): skip unpin when pinned shortcuts can't be read
* fix(launcher): drop in-flight app lists when home role is lost
* fix(launcher): only migrate folder entries with one launcher activity
* feat(setup): guide wireless pairing from a picture-in-picture window
* feat(setup): share one child-process fix flow between Setup and Play Steam
* fix(setup): read the child-process limit from the settings provider
* fix(setup): re-check the child-process limit while Setup is open
* fix(setup): skip a dead saved pairing and quiet the pairing guide
* fix(setup): handle repeat guide launches and start pairing after the display choice
The Choreographer frame callback that drives the compositor ran on the
app's main thread, so main-thread work delayed the tick before it
reached the compositor thread. It now runs on a display-priority
HandlerThread with its own Choreographer; the session pref vsyncThread
(default true) switches back to the main thread. Each resume posts a new
callback with its own generation, so one left queued from an earlier
resume, on either thread, stops itself instead of ticking twice.
A new 10 s "pacing" log line reports how late the tick reaches the
compositor, how long a new frame waits to be drawn, game frames against
buffers re-attached after an empty attach, empty attaches and distinct
buffers, and presentation feedback shown, replaced before drawing and
not drawn.
* fix: hold Home as Steam on Retroid Nova pads
* fix(input): track Home Guide apart from controller Guide
* fix(input): keep held controls on Home cancel and resume on Home
The WSI layer sends override_window_content on every present, and
gamescope walked the X tree to the top-level window (a blocking
XQueryTree round trip per level, under the wlserver lock) before
noticing the same swapchain already overrides it. A swapchain repeating
the same window now skips the walk for 100 ms (0121).
Backport of upstream gamescope 2962992 by Matthew Schwartz: the nested
Wayland backend's input thread reads the host display, but steamcompmgr
does not watch that fd, so buffer releases and presentation feedback
could wait up to one refresh for its next poll; the input thread now
wakes it after each read (0122).
Pins gamescope-3.16.29-p17, the archive that was A/B-tested on the
device. PATCHES.md also describes the p16 change to 0120.
The Steam helper checked the Frame Limit only when the offered frame
rates changed. With the limit off during a refresh change, turning it
back on left a value the new rate does not offer, and a failed write was
not tried again. It now checks on every state change and every 5 s, and
waits for Steam's settings write so a rejected write is caught and tried
again on the next check.
gamescope 3.16.29-p16 (0120 changed): gamescope took a new host refresh
only from the presentation feedback of a frame it drew, so with nothing
moving on screen it kept the old rate. It now presents once when the
host's wl_output mode changes and keeps presenting while a new rate is
pending, so the 300 ms check runs on a still screen too.
Found by CodeRabbit in the reviews of #1650 and #1663.
Game and global component choices were written to desired.tsv only when a
selection changed, so a Proton build installed later (DroidDeck-Proton
11-11.8) had no rows and kept its bundled FEX. Battle.net's d2rfix FEX never
reached it and StarCraft died in ClientSdk.dll's DllMain. The choices are now
republished at session start and on Battle.net's FEX request whenever the
installed tools no longer match what was published.
Remove the Performance mode switch. The session always runs the way
Performance mode did: replaced buffers go back to the game at once
(except while mirroring to glasses), gamescope's WSI layer and winex11
reuse their per-present window checks, and games get eight swapchain
images while uncapped. Steam's Frame Limit in the Quick Access menu is
the only limit.
gamescope 3.16.29-p15 (p14 plus two patches):
- 0119: with the Frame Limit on, gamescope forces FIFO while the WSI
layer hands the driver MAILBOX, so a game with eight images could
queue up to seven frames ahead of the screen. While the limiter forces
FIFO the layer uses its default of 3 images; DXVK and VKD3D-Proton
recreate their swapchain when the limiter changes, other apps are asked
to with VK_SUBOPTIMAL_KHR.
- 0120: -r fixed gamescope's pacing and limiter rate for the whole
session. With GAMESCOPE_NESTED_REFRESH_FOLLOWS_HOST=1 a rate the host
has reported for 300 ms replaces it (a switch from 144 to 120 Hz passes
through 72 Hz for a few frames), and Steam gets the display information
again. The session sets it whenever it passes -r.
Steam keeps its Frame Limit as the client setting
gamescope_app_target_framerate and offers the values gamescope's display
state lists. After a refresh change, a limit the new rate does not offer
(72 at 120 Hz) left the slider on its first value while the game kept the
old limit. The Steam helper watches the offered values and, once they
settle, keeps the limit when it is still offered and moves it to the
highest value otherwise, through Steam's own setting so the slider shows
it. Steam's webpack does not expose its module cache, so the gamescope
service is found by the calls its module makes, and the helper retries
the install every 10 s until Steam is ready.
The session window asks for the fastest mode the peak refresh setting
allows and chose again only when the display changed, so after it had
asked for 60 Hz, raising Android's refresh setting left the display,
gamescope and the limit at 60. It now also chooses again when
peak_refresh_rate or min_refresh_rate changes.
A capture format or buffer change now closes the app's decoder so the
next packet reopens it with the new stride and height, and a capture
slot whose remap fails no longer keeps its old mapping.
Steam's arm64 streaming_client only decodes through a V4L2 stateful
decoder and libdrm dumb buffers, neither of which exist on Android, so
streams from a PC never showed a picture. The preload now offers it a
/dev/video-dec0 and a libdrm stand-in backed by dma-heap buffers, and
each frame is decoded by the app's MediaCodec (Qualcomm low-latency
mode) over the encoder socket, falling back to the FFmpeg in Steam's
runtime.
skate. under ReSkate crashed with an access violation in Skate.exe 12 to
60 seconds after it started at the default feature level 12_2; at 12_1
it loads San Vansterdam and plays. The rule matches Skate.exe with
ReSkate.dll beside it, so it covers ReSkate started from skate.'s own
Steam entry and as a non-Steam game, and a game profile can still set
another level.
Proton sets WINE_CPU_TOPOLOGY from its own default_cpu_limit for games
like Prototype (8 cores) unless PROTON_CPU_TOPOLOGY is set, so the
pinned topology never reached the game the issue was about.
Game cores pinned the game with taskset, but Wine counts the online
CPUs, so a game held to two cores still saw eight processors and sized
its thread pools for them. Prototype freezes at its splash screen that
way. When the session or a game profile limits the game cores, Wine now
gets WINE_CPU_TOPOLOGY for exactly those cores unless the profile sets
its own.
* Add itch.io collections to the Library
A Collections button on the Library's itch tab filters the library to one collection of the signed-in account. Its games that are not installed show in the Not installed section.
* Keep itch collections complete across failures, sign-out and empty libraries
A failed later page no longer publishes a partial collection, selecting a collection refreshes it, pagination runs until an empty page, signing out clears collection state, and the itch chip shows for a signed-in account with no owned games.
* Tie itch collection requests to the account that started them
Requests capture the account generation before reading the key and dedupe per generation, sign-out clears collections after the token is removed, and the collections load follows the itch sign-in state.
* Keep paging itch collections past a page of already seen games
* Run the itch collections tests under Robolectric like the other itch tests
* Let the Installed filter apply inside a collection and keep the grid for an empty itch account
* fix(itch): stop paging a collection when a page adds no new games
* Keep the collections icon on the itch collections tab when it opens
---------
Co-authored-by: maxjivi05 <maxjividen@gmail.com>
The game surface only took touches in gestures mode, so in touchpad and
direct mode a finger on the game left the window with no touch target and
Android sent every later finger to the touchpad, even over a stick or
button. The surface now owns those fingers in every mode, and the
on-screen and Steam touch controls hand fingers that miss a control back
to the touchpad instead of dropping them.
* feat(input): support instant Back chords and tap timing
* fix(input): send QAM before shortcut inputs
* fix(input): stop delayed stick input on Back release
The Crew 2 loads D3DGear64.dll from its folder. Its DXGI Present hook
faults over a thousand times within seconds under Wine and the game
never presents again: black screen while the sound keeps playing.
droiddeck-game-env now disables the DLL when it finds it beside the
game, and DLL overrides from several engine fixes are kept together
instead of the last one replacing the others.
Steam's native QAM sliders now control Android brightness and media
volume, with device changes reflected back in Steam. Holding Android
Back for 700 ms sends a real held QAM button for Steam shortcuts. Single
and double taps keep the existing menu/QAM order and swap setting; the
double-tap window remains 500 ms.
- Discover native brightness through a guest backlight descriptor and
use Android's Modify system settings permission. Retain the latest
pending request until permission is granted or the session stops, with a
notification action for hidden sessions.
- Give DirectAudio and fallback AAudio sinks Android media volume/mute
callbacks, keeping software gain at unity. Recover when initial volume
state is absent or invalid, brightness acknowledgements differ or never
arrive, or the Display bridge is not ready yet.
- Forward other buttons while QAM is held in Steam Deck and Steam
Controller modes. Consume release after a hold and clear pending input
on overlays, focus loss, pause, disconnect, and teardown. The session
menu retains its compact Back choice without an explanatory paragraph.
Validation:
- Local ARM64 release build passed. The latest timing change passed 20
targeted input tests, including release at 600 ms remaining a tap. The
preceding full feature check passed 61 Android input/device-control
tests, with permission cases on API 28 and 33.
- 66 Linux Python/Node/C tests passed for device controls, audio
recovery, Steam compatibility, and file dialogs. Both audio sink modules
rebuilt with Android NDK 27.2.
- Release-signed integration build 14d12cf0 is installed on the unrooted
AYN Thor, carrying the Android/input changes through 749ade4a plus the
existing portrait changes. Device checks confirmed the configured
QAM/menu tap order, Back dismissal, removal of the inline menu
description, and held QAM overlapping X with clean release. Hands-on
testing confirmed this installed build works.
- Earlier Thor testing covered brightness permission and pending
requests, brightness synchronization in both directions, native volume
changes, hardware volume key readback, and both sink backends. Failure
recovery is covered by the automated tests above.
The subsequent queued-notification fix in 4d7ceb23 is covered by the
Linux regression suite and has not replaced the Thor install.
Bluetooth/wired output switching and other device models remain
untested. CI and CodeRabbit monitoring continue against the pushed head.
A tag that matches no longer vouches for the build: FEX and FEXServer must
also load (ld.so --list), otherwise the session reinstalls it and x86
programs use the Steam or system FEX meanwhile.
The pin moved there from droiddeck-steam-x64, so the client settings sheet
and its test read it from the new script. The x86-64 client notes now name
the updater and Components' FEX watcher.
A failed catalog install is retried after an hour or when the session names
another build, a missing updater falls back to the FEX already here, and the
archive download gives up on a stalled connection.
The Sentry GDExtension was turned off for every Godot game that ships it,
because its crash handler takes the .NET runtime down under FEX. GDScript
games such as Pathogenic call SentrySDK from their scripts, so without the
extension their autoloads fail to parse and the game quits. Only Godot .NET
builds have it turned off now.
The launch view lives under the app's files folder, so games saw an
install path like Z:\data\user\0\com.droiddeck.launcher\files\xbox-runtime
\views\launch-<n>\droiddeck-xbox-<id>. Forza Horizon checks that length and
quits with "Installation path is too long". The view is now mapped to a
free drive letter in the prefix (X: first) for the length of the launch.