Repository navigation
android: A57 — nice -20 through RLIMIT_NICE, without CAP_SYS_NICE - #30
Closed
utkarshdalal wants to merge 2 commits into
Closed
utkarshdalal wants to merge 2 commits into
utkarshdalal wants to merge 2 commits into
Conversation
added 2 commits
November 2, 2026 09:36
gamescope lowers its nice value only with CAP_SYS_NICE, which an Android app never holds, so every gamescope thread ran at nice 0 while the game's threads ran at -5/-2 and Wine's audio threads at -19. On a Retroid Pocket 6 (Android 13, Dave the Diver at a 60 cap) about 1 % of frames missed their vblank, 23 of 25 late posts over 44 s being gamescope's own commit coming 3.9-10.8 ms late with gamescope-xwm runnable but not scheduled. The device grants RLIMIT_NICE = 40 to the app and its children, which lets setpriority reach nice -20 without the capability. Use it for the nice path only: realtime scheduling and the REALTIME Vulkan queue stay on CAP_SYS_NICE (RLIMIT_RTPRIO is 0 there). RestoreNice no longer needs the capability, so Xwayland and the game still start at the inherited nice.
nice() changes only the calling thread, and g_ImageWaiter ("gamescope_img")
and g_LibEisWaiter ("gamescope-eis") are started by static constructors
before main sets nice -20, so they stayed at nice 0. On the RP6 (SteamWorld
Dig 2, 30 s live capture) gamescope_img first ran 15.5 ms after an Xwayland
commit arrived, the commit was not done at the next wakeup, and gamescope
skipped a whole period. SetNice now gives the new value to every other
thread still at the old one.
The header also records that Xwayland is forked by wlroots, not through
Process::SpawnProcess, so it inherits -20 as it does upstream.
4 of 8 tasks
Author
|
Closing: on its own this measured no fps change on the RP6 (daemon late count fell 4-6 -> 0-3 per 600 vsyncs, SF fps unchanged). The branch stays for a future A/B; the delivery is gamenative-drm commits for the real vsync, FIFO under a cap and the wp_presentation present wait. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Base
gamenative(f8edbc8), headnice-via-rlimit-nice(a051351). One new patch,android/patches/gamescope/0053-A57-nice-via-rlimit-nice.patch, in two commits (f63401b, a051351). CI: Android build (test) run 41394426789, green on a051351.What this fixes
gamescope lowers its nice value only when it holds CAP_SYS_NICE (
main.cpp→Process::SetNice(-20),Utils/Process.cpp). An Android app never holds that capability, so gamescope loggedNo CAP_SYS_NICE, falling back to regular-priority compute and threadsand every gamescope thread ran at nice 0. Next to it:NI -5andNI -2intop -H);Measured on a Retroid Pocket 6 (Android 13, Dave the Diver, DXVK, cap 60, 60 Hz mode, flip events on the display's real vsync from GameNative/gamenative-drm
flip-events-on-the-real-vsync-pr):gamescope-xwmrunnable but not scheduled for 1-8 ms.vblankmanager.cpptaking the spike as its new draw-time maximum, then decaying it 2 % a frame. Nothing outside gamescope can move its wakeup that way.The device grants the app and every child
RLIMIT_NICE= 40 (soft and hard) andRLIMIT_RTPRIO= 0. From/proc/<gamescope>/limits:That is how Linux lets an unprivileged process lower its nice value:
setprioritysucceeds down to20 − rlim_cur, which is −20 here. Realtime scheduling is not granted.The change (
src/Utils/Process.cpp)CanSetNice(n)is true with CAP_SYS_NICE, or with an RLIMIT_NICE soft limit that reachesn(20 − rlim_cur <= n, orRLIM_INFINITY).SetNiceuses it.maintherefore sets nice −20 on this path too, and every thread created after it inherits −20:gamescope-xwm,-wl,-kms,-wait,-vrmonand the vblank thread.main.nice()changes only the calling thread, and two waiter threads are started by static constructors beforemainruns:g_ImageWaiter(gamescope_img,steamcompmgr.cpp) andg_LibEisWaiter(gamescope-eis,wlserver.cpp). They stayed at nice 0, a gap upstream has with CAP_SYS_NICE as well.gamescope_img, the thread that marks an Xwayland commit done, first ran 15.5 ms after the commit arrived, on a CPU 67 % busy with app threads. The commit was not done at the next wakeup, and gamescope skipped a whole period.SetNicenow walks/proc/self/taskand gives the new value to every other thread still at the old one.SetNicerecords values only when it lowered them. The game and the other children gamescope spawns throughProcess::SpawnProcessstill start at the nice gamescope was started with.xwayland/server.c), not throughSpawnProcess, so it inherits −20, as it does upstream on a desktop with CAP_SYS_NICE.SetRealtimestays on CAP_SYS_NICE only (RLIMIT_RTPRIO is 0).VK_QUEUE_GLOBAL_PRIORITY_REALTIMEonly with CAP_SYS_NICE, with the existingVK_ERROR_NOT_PERMITTED_KHRfallback, so both queues stay at regular priority as before.vblankmanager.cpp,steamcompmgr.cppandDRMBackend.cppare not touched.Verified on device
Retroid Pocket 6, payload 20261101.6 with
bin/gamescopereplaced by this branch's CI artifact (sha256b5b23f33…), SteamWorld Dig 2 and Dave the Diver.At start, gamescope logs this instead of the "falling back to regular-priority" line:
Per-thread nice values during the session:
gamescope-wl,gamescope_img,gamescope-kms,gamescope-xwm,gamescope-wait,gamescope-vrmonzfq0,zcq0,zcfq0-2,gdrv0,gl0disk$Nandtraceq0at 19 (Mesa's own background queues, which set their own nice)Before the patch, every one of those gamescope and Xwayland threads was at nice 0.
This patch ships as part of payload 20261102.1, together with the gamenative-drm changes (flip events on the display's vsync, FIFO under a cap, present wait from
wp_presentation) and the app's compositor-chain affinity. With all three, on the RP6 at cap 60:This patch's own share of those numbers has not been isolated. What it targets is the gamescope-side draw spikes and the late
gamescope_imgdescribed above.How to check
On any device,
cat /proc/$(pidof -s gamescope)/limits | grep -i 'nice\|realtime'shows the limit the patch relies on. Then:Every gamescope thread should read −20. Where RLIMIT_NICE is below 40,
CanSetNice(-20)is false, nothing changes, and the old log line remains.