On phones where Android blocks the GPU's sysfs files for apps, the HUD
reads the GPU without root (#1969). On Adreno 8xx that showed no GPU
clock and only the partial load from Vulkan timing (OnePlus 13, Adreno
830, reported by iamTooShaar).
- GPU clock: the clock job used the Adreno 6xx/7xx registers. The
register layout is now picked from the KGSL chip id: on 8xx
CP_ALWAYS_ON_COUNTER is 0x8e7 and the RBBM cycle countable is 1.
Unknown later generations read nothing.
- GPU load: hardware-scheduled kgsl kernels (every 8xx, and some 7xx
such as the AYANEO Pocket FIT's) never write the memstore ringbuffer
timestamps. When those stay 0, the reader samples the contexts' own
memstore entries (every context that has submitted work, up to 64,
contexts with new or running work first).
- HUD diagnostics report: a "GPU readers without root" line with the
reason each reader works or fails, "none" instead of "null", and the
per-core CPU % source named honestly.
Tested: iamTooShaar's OnePlus 13 shows GPU clock and load with the PR
test build (06be2c5). Pocket FIT standalone build of the final readers
against the kernel's sysfs numbers: idle 1-2 % vs 3-4 %, GPU job loop
25-29 % vs 28-30 %, clock 1050 MHz vs 1050 MHz. The review fixes after
the test build were not tested on the OnePlus 13.
DOOM (2016) asks for exactly two swapchain images and stops with "FATAL
ERROR: getSwapchainImagesKHR failed with error (VK_SUCCESS)" when it gets
more. Every game launch gets GAMESCOPE_WSI_MIN_IMAGE_COUNT=8 and
GAMESCOPE_WSI_ENSURE_MIN_IMAGE_COUNT=1, so gamescope's WSI layer gave it 8
images (3 with only the count removed) and the game crashed on start.
When DOOMx64vk.exe sits beside the launched program (DOOMx64.exe for a GOG
install, or DOOMx64vk.exe), both variables are removed and
vk_x11_strict_image_count=true is set, the option Mesa's driconf gives the
game. Other games keep the defaults. A per-game value in the environment
editor still wins.
Tested on an AYANEO Pocket FIT with DroidDeck 0.6.9 and the same values set
per game: the swapchain gets 2 images and the game reaches its main menu at
60 fps; before, it crashed on every launch.
* feat: manage several Steam libraries beside internal and the card
* fix: serialize Steam library registry edits, refuse folders inside a Games root, refresh games after changes
* fix: rename refreshes games, added x86-64 libraries join redist seeding and direct mapping
* chore(i18n): translate every missing string in all locales
Adds the 87 strings the bottom-screen Dashboard and in-game stats pages
introduced to all 32 locales, plus the 51 older settings strings Japanese
and the 50 Chinese locales were still missing. Every locale now has all
3456 translatable strings with the same placeholders as English.
Also translates 36 entries that were still English where the language
does not use the English word, and makes es-419 touch_app_id use the same
%1$s placeholder as English.
* chore(patches): drop comments from the lines downstream patches add
Removes comment lines and trailing comments from lines our patches add,
with hunk headers recounted. Lines that a later patch uses as context
and the upstream backport 0123 are left as they are.
The gamescope, FEX, proot and xodus series still apply to their pinned
sources, and the patched trees differ only in those comments.
test_hud_frames now finds the HUD writer by its first declaration.
* docs: credit Greenlight, Better xCloud, viture-v2, GameNative and DiRTTelemetryErrorFix
These were credited only in source headers and NOTICE files.
* chore: remove source comments and unused functions
Removes explanatory comments from first-party Kotlin, Java, C, C++,
Rust, TypeScript, Python, shell, Gradle, CMake and workflow files.
License and attribution headers stay, and vendored third-party trees,
generated files and license files are untouched. A shellcheck directive
is kept, and udevmon's fallthrough comments became fallthrough attributes.
Syntax trees and token streams match the previous sources apart from the
comments and Python docstrings. Module docstrings that argparse prints
are kept, and device-smoke.sh prints the same --help text from a heredoc.
Also removes 37 functions and 4 fields that nothing referenced.
* chore: remove comments from the Syncthing integration and the latest main sources
* docs: credit Syncthing and the syncthing-android fork
* chore(i18n): translate the Syncthing strings in all locales
The 107 strings the Syncthing integration added are now in all 32 locales.
sync_web_path_locked names the Syncthing tab by its label.
* docs: credit @xXJSONDeruloXx for the HUD metric readers
The CPU, GPU load, thermal and battery readers come from their
PerformanceHudView pull requests #777, #877, #991, #1406 and #1573.
The FusionHUD credit line is unchanged.
* docs: credit the HUD metric readers to @xXJSONDeruloXx's work
* feat: KDE desktop as a bottom-screen mode beside the top-screen session
* test: bottom-desktop launch test sets DISPLAY instead of inheriting it
* fix: drop the bottom desktop's launch when the session stops while preparing it
* fix: bottom desktop restarts on a fresh surface, trackpad/touch toggle, loading throbber, DroidDeck's display driver
* fix: bottom desktop pauses with the session and opens the keyboard for a tapped text field
* fix: no bottom desktop guest starts or keeps running unfrozen while the session is paused
* fix: bottom desktop treats a freeze in progress as paused
* fix: only the session that began a freeze clears its pausing flag
* fix: bottom desktop launch is stored and cleared only for its own session generation
* fix: a stopped session tears down the bottom desktop it started even after a newer launch is prepared
* fix: a new session's bottom desktop replaces one an earlier session left running
* fix: release the bottom desktop's keyboard watcher when its compositor dies
* fix: bottom desktop starts its guest only for the launch its compositor was attached for
* fix: bottom desktop ignores compositor replies to an earlier session's attach
On some Adreno devices (the Retroid Pocket Duo) SELinux hides kgsl's sysfs
nodes from apps, so the HUD showed no GPU % and no GPU clock. Devices whose
nodes are readable keep using them; nothing changes there or on GPUs
without kgsl.
Where the nodes are blocked:
- GPU %: a native reader maps kgsl's memstore read-only (any kgsl client
may) and samples the ringbuffer start/end timestamps once a millisecond
while a HUD is shown. That covers all GPU work, Android's own
composition included.
- Backup for GPU %: an implicit Vulkan layer in the guest
(tools/gpuload) and the compositor time their own GPU work with
timestamp queries. It runs until the memstore stamps are seen moving,
and for the whole session if they never move.
- GPU clock: a small GPU job of our own reads the always-on and RBBM
cycle counters (libgpuclock).
The HUD diagnostics export lists which source each GPU value came from.
tools/gpuload/build.sh takes the Vulkan headers from build_local.sh's NDK
first and keeps NDK paths with spaces intact.
Codemasters EGO games (DiRT 3, DiRT Showdown, GRID, F1 ...) pick their thread plan by core count from system/hardware_settings_restrictions.xml, which only has plans for 2, 3, 4, 6 and 8+ cores. When DroidDeck leaves a phone's slow cores out Windows can see 5 (Retroid Pocket Duo), and DiRT 3 then puts every worker on one core and stops on a loading screen; Showdown closes at start.
When a game starts with WINE_CPU_TOPOLOGY set and its restrictions have no cpu rule for that count, droiddeck-game-env adds one copied from the nearest smaller plan (5 -> the game's 4-core plan), only if that plan's worker map file exists. The file as it was is kept as .droiddeck-original; later launches change nothing. Only ASCII digits count as a core count, and the edited file is staged under a unique name.
Tested on a Retroid Pocket Duo: with DiRT 3's shipped file, the plan is added at launch and the game loads to its menus and races.
A Dashboard mode for the second screen of dual-screen handhelds (developed on a Retroid Pocket Duo), the default on dual-screen devices. It opens on Chat at each Steam start and switches to In-game stats when a game starts.
- Downloads: Steam downloads and updates (SD-card libraries too), shader caches and DroidDeck store downloads.
- Notifications: other apps' notifications once access is allowed, kept in memory only.
- Screenshots: a game screenshot without the overlay, and a gallery of DroidDeck's and Steam's screenshots.
- Chat & voice: mic and game volume buttons, and Steam's web chat signed in with the Steam client's own session.
- In-game stats: FPS, CPU, GPU, RAM and battery plus the running game or Steam client type, and a racing dash from Codemasters, Forza and OutGauge games' data output, with War Thunder and Elite Dangerous layouts; speed in km/h or mph.
- Send dashboard data: turns a Codemasters game's data output on for the dash (per game or Automatic) and keeps the original setting to put back.
- DiRT 3 / DiRT Showdown: with their data output on they stop with error 10038 at race end; a ws2_32.dll stand-in (tools/dirt-ws2) is swapped into the prefix while the game runs, skipped on DroidDeck-Proton 11-11.10 which has the fix built in. Same behaviour as DiRTTelemetryErrorFix by RoccoC, based on Carsten Gottschling's fix.
About "telemetry": here it means a racing game's own live driving data (speed, gear, rpm, pedals), not app analytics. For In-game stats no information leaves the device and nothing is shared: the game sends its data to 127.0.0.1 on the same device, DroidDeck only listens there while the page is open and keeps the numbers in memory just long enough to draw them. DroidDeck sends no data about the user or their use of the app; logs leave the device only when the user taps Share logs.
* Run x86_64 Flatpak apps through FEX
* proot: run an execveat with an absolute path like execve
* Find x86_64 Flatpak installs and resolve sandbox paths like the sandbox does
* fix: a read-only file on a USB drive no longer fails to close the first time it is read
On the RedMagic Astra 2, Android's NTFS USB drive returns EIO from close() for a file opened read-only when that first read updates its access time; the update itself succeeds and the descriptor is released. The x86 loader under FEX treats a failed close as fatal, so every Linux game newly installed or updated on such a drive failed to start once (libfreetype.so.6: cannot close file descriptor: Input/output error). The session library's close() now reports success for EIO on read-only descriptors; errors on descriptors opened for writing are still returned.
* Check the read-write close still reports EIO
* fix(audio): DirectAudio no longer crackles where Android refuses low-latency output
On devices where Android won't give the game a low-latency output (AYN Thor,
Android 13), DirectAudio kept Wine's 10 ms period and 12 ms buffer. An
emulated game audio thread can't keep that pace there, so audio crackled.
The session now checks for low-latency output and, only when it is refused,
asks DirectAudio for a 20 ms period and a 60 ms buffer.
DirectAudio's settings are now AUDIO_DIRECT_* everywhere in DroidDeck and are
handed to the driver under the names it reads when DirectAudio is set up.
AUDIO_DIRECT_MS and AUDIO_DIRECT_PERIOD_MS can be set per game.
* fix(audio): game profile DirectAudio settings and the microphone-off setting reach the driver
* fix(desktop): video wallpapers no longer race ahead after DroidDeck returns from the background
* test: give the vrclient loader harness the clock syscall hook
* fix: a session in picture-in-picture draws at 30 fps instead of the full refresh
Since auto picture-in-picture became the default (#1727), pressing Home leaves the session running in a small window that takes no input. A game kept drawing at its full rate there: on a REDMAGIC Astra 2 Among Us in picture-in-picture drew 3.31 W, the same as playing it and about 1.05 W more than a suspended session. While in picture-in-picture the compositor now paces the session at 30 fps (or a lower limit already set). Mirroring to glasses keeps the glasses rate, and the display mode request ignores the picture-in-picture limit so the app in front is not slowed.
* fix: keep the no-argument output limit the glasses pacing tests read
* fix: hold scene renders to the output limit and keep frame generation off in picture-in-picture
A frame limit only paced dmabuf buffer releases, so a shared-memory client was still drawn on every vsync with its frame callbacks sent at the panel's rate. The vsync tick now waits until the limit's interval, less half a refresh, has passed since the last scene. Frame generation is disarmed while picture-in-picture is shown and armed again when it ends; frame generation requests are applied in order.
* Give the commit render harness the frame limit hold
* Hold scene renders to a frame limit deadline, also on the fallback timer, and keep frame generation for glasses mirroring in picture in picture
* fix(xbox): keep the game in its own folder on the short drive instead of at the drive root
* fix(xbox): SD card Xbox games can read their own package files
Xodus mounts a store library on the selected SD card at /mnt/droiddeck-stores/<card> and links the launch view to it, but game sessions only bound that library as /mnt/droiddeck-sd. The links were dangling inside the game, so MicrosoftGame.Config, appxmanifest.xml and XCurl.dll could not be read and GDK games stopped with "Failed to initialize GDK". Game sessions now bind the same store roots Xodus uses.
* fix(xbox): give the SD Steam library its own store path when another store root shares the card
externalRootsForXodus added the selected Steam library's Games folder with the card's guest path even when another store root on the same card already used it, so both bound to one destination. It now takes a path-derived suffix in that case, the same way externalRoots separates its own roots.
* perf(epic): keep the hidden launcher from loading its store and Fab pages, and compress it while games run
The hidden Epic Games Launcher that stays up for sign-in while a game runs pre-warmed its store and Fab pages on a display nobody sees. Under FEX each page used about one core and grew past a gigabyte, crashing and reloading in a loop. The hidden instance now skips the store pre-warm, gets a blank page in place of Fab, and is paged out to zRAM with the Steam client once a game has focus. The visible launcher is unchanged.
* test(zram): keep the scan fixture PIDs above the kernel PID range
* Use the second screen on dual-screen handhelds
Detect built-in second screens such as the Retroid Pocket Duo and AYN Thor.
The sections move to a menu on the bottom screen, L2 and R2 switch sections,
and text fields type on the bottom screen so the top keeps the full view.
Sessions open the bottom-screen trackpad and keyboard when they start.
Setup gains a Dual screen choice (Automatic, On, Off) with toggles for the
bottom menu and the in-game controls. Closing the bottom menu no longer ends
a running session.
* Type into Steam from the bottom screen
While the bottom-screen trackpad and keyboard controls are open in a Steam
session, Steam's own on-screen keyboard no longer covers the top screen.
The agent keeps Steam's text target and asks the app instead, which opens
the Android keyboard on the bottom screen and hands focus back to the game
when it closes. Modal and VR keyboards, and sessions without the bottom
controls, keep Steam's keyboard. The Show keyboard button now follows the
keyboard when it is closed another way.
* Open the bottom controls after the first frame and tidy dual-screen translations
* Move the in-game menu to the bottom screen on dual-screen handhelds
Fix controller input and focus on the second screen: keys and sticks reach the game after touching the bottom controls, closing the bottom controls returns focus to the game, B closes bottom typing, and the typing panel keeps a focusable window until focus has left it.
* Let B close the open layer of the session menu first and focus the selected bottom-screen mode
* Let the Retroid Pocket Duo Home double press open its quick menu during sessions
* Open the keyboard on the bottom screen for Steam text fields and hide it on Enter or when the field loses focus
* Keep controller focus on the top screen when a section is picked on the bottom screen
* Keep LB and RB on the store tabs and let Back return from a store to its tab
* Show the Adreno 722 as supported on the Retroid Pocket Duo
* Label the Steam integration suggestion's close button Close and cover that closing it is permanent
* Close the docked menu with its presentation, keep a focus picked during the stop delay, and ignore panel picks on process-settings pages
* perf(fex): answer raw stat, readlink and statx syscalls through the proot fast path
FEX issues newfstatat, readlinkat and statx as raw syscall() calls, which skipped the libc wrappers the fast path hooks and fell through to ptrace. Route them through libblfastpath the same way raw openat already is, and keep the fast path's own /proc/self/exe lookup off the hooked syscall().
* test(fex): keep the raw lookup test passing on kernels without statx
The session HUD is FusionHUD (Bannerlator's HUD) with every reading taken from
the same source, and worked out the same way, as the MangoHud build that runs
Steam's performance overlay in a DroidDeck session.
- gamescope patch 0125 (gamescope-3.16.29-p19) shares the frame times that
mangoapp reads (one per signalled commit of connector 0) in a ring named by
DROIDDECK_HUD_FRAMES; the app reads it once per display frame and counts the
compositor's frames when gamescope sends none.
- MangoHud's rules: FPS over periods of at least 500 ms, frame time = 1000/FPS,
200-frame graph on a fixed 0-50 ms scale; CPU, GPU, temperatures, RAM, swap,
VRAM and battery from MangoHud's sources. Zones 22/26/28 are typed as GPU and
DDR sensors, so Steam's overlay CPU temperature no longer includes them.
- Five sizes in a panel shaped like the DroidDeck mark, with a centred header,
one left edge, neon outline and FPS; Minimal is the default size. Settings on
a new "Performance HUD" drawer page, with Bannerlator's options.
- The HUD reads sensors and polls frames only while it is drawn (not while
hidden, in picture-in-picture or behind another app), keeps its place after
picture-in-picture, and the diagnostics export has its own reader.
Device-tested on the AYANEO Pocket FIT: same readings as Steam's overlay side
by side in Dead Cells; the HUD updates in a game, hides in picture-in-picture
and comes back in place, no crashes.