Files
DroidDeck/tools/linuxfs/preload/kgslpoll.c
T
MaxsTechReview 5251c6a5f6 Additional performance fixes (#87)
* perf: answer Turnip's zero-timeout KGSL timestamp polls without blocking

Turnip polls a timeline point with IOCTL_KGSL_DEVICE_WAITTIMESTAMP_CTXTID
and a timeout of 0, which KGSL reads as wait forever; Mesa does it under
a lock on every signalling submit, so vkd3d-proton, DXVK, gamescope and
Zink ran in lock-step with the GPU. The session preload now answers
those polls from the context's retired timestamp and passes everything
else to the kernel. Bannerlator measured a D3D12 demo at 614 -> 4335 fps
with the same answer in its Vulkan adapter. BL_NO_KGSL_POLL_FIX=1 turns
it off.

* perf: frame limiter, live panel refresh, opt-in GPU clock pin, cheaper cursor sync

- A per-mode frame limit (Steam and desktop settings, off by default):
  gamescope's -r, the compositor's buffer-release pacer, the display
  layer's frame-rate vote and a panel mode the cap divides evenly, as
  WinNative's per-shortcut limit does. BL_FPS was always 0 and the
  pacer's limit was never set.
- The compositor follows the panel's refresh rate when it changes
  mid-session instead of reporting the rate read at start. (WinNative.)
- Performance > GPU: hold the Adreno clock at its top through KGSL's
  power-control property, no root (Bannerlator's PerfGpuTurbo). Off by
  default; cleared when the session stops and at the next app start if
  a killed process left it set.
- The cursor image is copied across JNI only when its serial changes,
  not on every pointer move.

* perf: stop re-seeding every prefix every 5 s, and run the library helpers only on change

- bannerlator-seed-redists stamped its change check from a block that
  carried the current time, so it never matched: every prefix had ~32
  keys appended to system.reg on every beat. The stamp now comes from
  the names; Proton's prefix template is seeded too (date kept), with
  WinNative's built-in list until the client has the install scripts.
- The library beat runs the seeder every 15 s and steam-compat only
  when a directory it reads has changed, or once a minute.
- The client-core pin reads /proc itself and runs taskset -a only for
  new processes or ones that grew threads.
- The games watcher backs off after a failed ownership probe and does
  not probe while a game runs.
- The Proton launcher runs the newest ARM64 depot by build number, and
  PROTON_USE_PIPEWIRE defaults to 0. (WinNative.)

* perf: Mesa's database shader cache, and proot's tracer above the guest

- MESA_DISK_CACHE_DATABASE=1 for the session: the file cache opens a
  file per lookup and opens and renames one per store, each a proot
  stop; the database is opened once and used with pread/pwrite.
- proot's single tracer thread is raised to nice -6 (under the
  compositor's -8) two seconds after it starts, so the first guest
  process it forks keeps nice 0. setpriority() directly, not
  Process.setThreadPriority, which would also move its cgroup.
2026-09-30 16:43:52 -04:00

80 lines
2.9 KiB
C

/*
* A zero-timeout KGSL timestamp wait answered as the poll it was meant to be.
*
* Turnip's KGSL backend emulates timeline semaphores with timestamps, and asks "has this one
* retired yet?" by waiting on it with IOCTL_KGSL_DEVICE_WAITTIMESTAMP_CTXTID and a timeout of 0.
* KGSL reads a timeout of 0 as no timeout at all, so the poll blocks until the GPU gets there.
* Mesa's timeline bookkeeping polls like that under a lock on every signalling submit, which put
* vkd3d-proton, DXVK, gamescope and Zink in lock-step with the GPU: one frame queued, never two.
* Found and measured by Bannerlator (PROGRESS_LOG 2026-09-29: a D3D12 demo went from 614 to
* 4335 fps with the same answer given in its Vulkan adapter); the driver-side fix is on
* Banners-Turnip, and this covers the drivers that do not carry it yet.
*
* The answer comes from the context's retired timestamp, compared the way KGSL compares them
* (with wrap-around). Anything the read does not answer - an unknown context, a device that is not
* KGSL - goes to the kernel as before. BL_NO_KGSL_POLL_FIX=1 turns it off.
*/
#define _GNU_SOURCE
#include <errno.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#define KGSL_IOC_TYPE 0x09
#define KGSL_TIMESTAMP_RETIRED 0x00000002
struct kgsl_device_waittimestamp_ctxtid {
unsigned int context_id;
unsigned int timestamp;
unsigned int timeout;
};
struct kgsl_cmdstream_readtimestamp_ctxtid {
unsigned int context_id;
unsigned int type;
unsigned int timestamp;
};
#define IOCTL_KGSL_DEVICE_WAITTIMESTAMP_CTXTID \
_IOW(KGSL_IOC_TYPE, 0x7, struct kgsl_device_waittimestamp_ctxtid)
#define IOCTL_KGSL_CMDSTREAM_READTIMESTAMP_CTXTID \
_IOWR(KGSL_IOC_TYPE, 0x16, struct kgsl_cmdstream_readtimestamp_ctxtid)
static int enabled(void) {
static int cached = -1;
const char *off;
if (cached >= 0) return cached;
off = getenv("BL_NO_KGSL_POLL_FIX");
cached = !(off != NULL && off[0] == '1');
return cached;
}
/* True when the call was answered here, with its result in *rc (and errno set on failure). */
__attribute__((visibility("hidden"))) int bl_kgsl_poll(int (*real)(int, unsigned long, void *),
int fd, unsigned long request, void *arg,
int *rc) {
const struct kgsl_device_waittimestamp_ctxtid *wait = arg;
struct kgsl_cmdstream_readtimestamp_ctxtid read;
int saved = errno;
if (request != IOCTL_KGSL_DEVICE_WAITTIMESTAMP_CTXTID || arg == NULL || wait->timeout != 0 ||
!enabled())
return 0;
read.context_id = wait->context_id;
read.type = KGSL_TIMESTAMP_RETIRED;
read.timestamp = 0;
if (real(fd, IOCTL_KGSL_CMDSTREAM_READTIMESTAMP_CTXTID, &read) != 0) {
errno = saved;
return 0;
}
if ((int32_t)(read.timestamp - wait->timestamp) >= 0) {
errno = saved;
*rc = 0;
} else {
errno = ETIMEDOUT;
*rc = -1;
}
return 1;
}