From 0e2a2dde61427e3bc13bfae55b1f1897504bef25 Mon Sep 17 00:00:00 2001 From: Andras Laszlo Date: Fri, 16 Oct 2026 17:50:22 +0200 Subject: [PATCH] Address code review feedback on input handling, pacing, and runtime cleanup --- patches/cocotron.patch | 16 +++--- patches/darling.patch | 5 +- patches/metal--deps--indium.patch | 6 +-- patches/xnu.patch | 11 ++-- scripts/installer.sh | 1 - tools/ensure-darling-runtime.py | 83 +++++++++++++++++-------------- tools/m58f299-xshm.c | 8 ++- tools/riot-desktop-entry.py | 2 +- 8 files changed, 68 insertions(+), 64 deletions(-) diff --git a/patches/cocotron.patch b/patches/cocotron.patch index caa7a26..0886f3c 100644 --- a/patches/cocotron.patch +++ b/patches/cocotron.patch @@ -2307,7 +2307,7 @@ index 18189404..c683f96a 100644 +- (NSRect) frameForRootRect: (NSRect) rect; @end diff --git a/AppKit/X11.backend/X11Display.m b/AppKit/X11.backend/X11Display.m -index 5ae29bf4..516decf3 100644 +index 5ae29bf4..d7fb09fc 100644 --- a/AppKit/X11.backend/X11Display.m +++ b/AppKit/X11.backend/X11Display.m @@ -48,12 +48,271 @@ @@ -3125,7 +3125,7 @@ index 5ae29bf4..516decf3 100644 + int xfd = ConnectionNumber(_display); + struct pollfd pfd = { .fd = xfd, .events = POLLIN }; + int timeout_ms = (int)(slice * 1000.0); -+ if (timeout_ms < 1) timeout_ms = 1; ++ if (timeout_ms < 0) timeout_ms = 0; + extern long __lolsys_syscall(long, long, long, long, long, long, int); + __lolsys_syscall((long)&pfd, 1, timeout_ms, 0, 0, 0, 7 /* Linux poll */); + } @@ -3391,7 +3391,7 @@ index 5ae29bf4..516decf3 100644 [self postEvent: event atStart: NO]; break; -@@ -1160,49 +1855,113 @@ static NSDictionary *modeInfoToDictionary(const XRRModeInfo *mi, int depth) { +@@ -1160,49 +1855,111 @@ static NSDictionary *modeInfoToDictionary(const XRRModeInfo *mi, int depth) { // ev->xmotion.x, ev->xmotion.y, ev->xmotion.x_root, // ev->xmotion.y_root); @@ -3418,8 +3418,6 @@ index 5ae29bf4..516decf3 100644 + ev->xmotion.x = clampedX; + ev->xmotion.y = clampedY; + } -+ } else if (!_relativeMouse.active && window != nil && !_confinedWindow) { -+ [self updateMouseConfinementForWindow: window]; + } + CGPoint lastMotionPos = [window mouseLocationOutsideOfEventStream]; @@ -3531,7 +3529,7 @@ index 5ae29bf4..516decf3 100644 mouseEventWithType: type location: pos modifierFlags: -@@ -1211,28 +1970,58 @@ static NSDictionary *modeInfoToDictionary(const XRRModeInfo *mi, int depth) { +@@ -1211,28 +1968,58 @@ static NSDictionary *modeInfoToDictionary(const XRRModeInfo *mi, int depth) { clickCount: 1 deltaX: deltaX deltaY: deltaY]; @@ -3592,7 +3590,7 @@ index 5ae29bf4..516decf3 100644 NSLog(@"FocusIn"); if ([delegate attachedSheet]) { [[delegate attachedSheet] makeKeyAndOrderFront: delegate]; -@@ -1243,28 +2032,59 @@ static NSDictionary *modeInfoToDictionary(const XRRModeInfo *mi, int depth) { +@@ -1243,28 +2030,61 @@ static NSDictionary *modeInfoToDictionary(const XRRModeInfo *mi, int depth) { checkForAppDeactivation: NO]; lastFocusedWindow = nil; } @@ -3620,7 +3618,7 @@ index 5ae29bf4..516decf3 100644 + rclolFocusDetailName(ev->xfocus.detail), _relativeRequested, _relativeMouse.active, + (unsigned long) _confinedWindow, + [NSApp isActive]); -+ if (ev->xfocus.detail == NotifyPointer || ev->xfocus.mode == NotifyGrab || ev->xfocus.mode == NotifyUngrab) { ++ if (ev->xfocus.detail == NotifyPointer || ev->xfocus.detail == NotifyInferior || ev->xfocus.mode == NotifyGrab || ev->xfocus.mode == NotifyUngrab) { + /* Pointer boundary crossing or grab transition in XWayland: do NOT release confinement or deactivate window */ + break; + } @@ -3637,6 +3635,8 @@ index 5ae29bf4..516decf3 100644 + if (_confinedWindow) { + XUngrabPointer(_display, CurrentTime); + _confinedWindow = None; ++ _confinedWidth = 0; ++ _confinedHeight = 0; + } + _otherButtonsDown = 0; if (window != nil) diff --git a/patches/darling.patch b/patches/darling.patch index 82a4fdc..312f184 100644 --- a/patches/darling.patch +++ b/patches/darling.patch @@ -6949,7 +6949,7 @@ index ffd7fe054..a74ffc63f 100644 + uint64_t current=(uint64_t)now.tv_sec*1000000000ull+now.tv_nsec; + uint64_t deadline=ctx->native_next_present_ns; + static uint64_t paceNS = 0; -+ if (!paceNS) { const char *hz = getenv("RCLOL_REFRESH_HZ"); int r = hz ? atoi(hz) : 0; paceNS = r > 0 ? 1000000000ull / (uint64_t)r : 1000000ull; } ++ if (!paceNS) { const char *hz = getenv("RCLOL_REFRESH_HZ"); int r = hz ? atoi(hz) : 0; paceNS = (r > 0 && r <= 1000) ? 1000000000ull / (uint64_t)r : 1000000ull; if (paceNS < 1000000ull) paceNS = 1000000ull; } + if(!deadline || (current>deadline && current-deadline>paceNS))deadline=current; + if(deadline>current){ + struct timespec wake={deadline/1000000000ull,deadline%1000000000ull}; @@ -8534,7 +8534,8 @@ new file mode 100644 + if (!refreshNS) { + const char *hz = getenv("RCLOL_REFRESH_HZ"); + int rate = hz ? atoi(hz) : 0; -+ refreshNS = rate > 0 ? 1000000000ull / (uint64_t)rate : 1000000ull; ++ refreshNS = (rate > 0 && rate <= 1000) ? 1000000000ull / (uint64_t)rate : 1000000ull; ++ if (refreshNS < 1000000ull) refreshNS = 1000000ull; + } + deadline += refreshNS; + uint64_t now=nativeMonotonicNS(); diff --git a/patches/metal--deps--indium.patch b/patches/metal--deps--indium.patch index f72c88d..c61e985 100644 --- a/patches/metal--deps--indium.patch +++ b/patches/metal--deps--indium.patch @@ -7450,11 +7450,7 @@ new file mode 100644 + static constexpr VkDeviceSize BlockSize = 32ull << 20; + static constexpr VkDeviceSize MaxPooledSize = 4ull << 20; + static constexpr VkDeviceSize Granule = 256; -+ // a freed range is not handed out again before this long: work already submitted to the GPU may still read it -+ /* darling-lol perf: the old 1000ms quarantine caused pool exhaustion -+ * and fallback OS allocations during high-churn gameplay. 50ms (~3 -+ * frames at 60Hz) is sufficient for in-flight GPU work to drain. */ -+ static constexpr std::chrono::milliseconds Quarantine {50}; ++ static constexpr std::chrono::milliseconds Quarantine {1000}; + + struct Block { + VkDeviceMemory memory = VK_NULL_HANDLE; diff --git a/patches/xnu.patch b/patches/xnu.patch index b317b3e..0bd1ad0 100644 --- a/patches/xnu.patch +++ b/patches/xnu.patch @@ -5893,11 +5893,14 @@ index b998456..2783a4a 100644 + __linux_sched_yield_site("SWTCH"); return 0; } - -@@ -696,6 +1613,48 @@ kern_return_t syscall_thread_switch_impl( - .tv_sec = 0, +@@ -693,11 +1610,53 @@ kern_return_t syscall_thread_switch_impl( + mach_msg_timeout_t option_time) + { + struct timespec tv = { +- .tv_sec = 0, - .tv_nsec = 1000000 -+ .tv_nsec = option_time > 0 ? (long)option_time * 1000 : 1000000 ++ .tv_sec = option_time > 0 ? (time_t)(option_time / 1000000) : 0, ++ .tv_nsec = option_time > 0 ? (long)((option_time % 1000000) * 1000) : 1000000 }; + + /* darling-lol: opt-in trace (host file /tmp/rclol-select-trace) of who calls thread_switch and with which option: diff --git a/scripts/installer.sh b/scripts/installer.sh index d9d1268..0d19df5 100755 --- a/scripts/installer.sh +++ b/scripts/installer.sh @@ -14,7 +14,6 @@ export RIOT_LAUNCHER_HOST RCLOL_REGION # client fails under Darling, so start the installed client here. sh scripts/run.sh || true unset RIOT_LAUNCHER_HOST -python3 build/runtime/tools/riot-desktop-entry.py || true prefix=${DPREFIX:-$HOME/.darling} if [ ! -d "$prefix/Users/Shared/Riot Games/Riot Client.app/Contents/MacOS" ]; then echo "The installer exited without installing the Riot Client. Its logs are in" >&2 diff --git a/tools/ensure-darling-runtime.py b/tools/ensure-darling-runtime.py index a34ff7f..d475114 100644 --- a/tools/ensure-darling-runtime.py +++ b/tools/ensure-darling-runtime.py @@ -1,47 +1,54 @@ #!/usr/bin/env python3 -"""Clear only a dead Darling server's stale PID marker before startup.""" +"""Clear only a dead Darling server's stale PID marker and buffers before startup.""" import os from pathlib import Path import time + prefix = Path(os.environ.get('DPREFIX', str(Path.home() / '.darling'))) marker = prefix / '.init.pid' -try: - pid = int(marker.read_text().strip()) -except (OSError, ValueError): - raise SystemExit(0) -if pid <= 0: - raise SystemExit(0) -status = Path('/proc') / str(pid) / 'status' -try: - state = next(line.split()[1] for line in status.read_text().splitlines() if line.startswith('State:')) - dead = state == 'Z' -except FileNotFoundError: - dead = True -except (OSError, StopIteration): - dead = False -if dead: - os.replace(marker, prefix / f'.init.pid.stale-{pid}-{time.time_ns()}') - print(f'Recovered stale Darling server marker for PID {pid}', flush=True) -# Clean up stale Riot temporary files and shared memory frames from previous crashes -try: - tmp_dir = prefix / 'private/tmp' - if tmp_dir.is_dir(): - for f in tmp_dir.glob('.com.riotgames.*'): +server_running = False +if marker.exists(): + try: + pid = int(marker.read_text().strip()) + if pid > 0: + status = Path('/proc') / str(pid) / 'status' try: - f.unlink(missing_ok=True) - except OSError: - pass -except OSError: - pass + state = next(line.split()[1] for line in status.read_text().splitlines() if line.startswith('State:')) + dead = state == 'Z' + except FileNotFoundError: + dead = True + except (OSError, StopIteration): + dead = False -try: - shm_dir = Path('/dev/shm') - if shm_dir.is_dir(): - for f in shm_dir.glob('darling-*'): - try: - f.unlink(missing_ok=True) - except OSError: - pass -except OSError: - pass + if dead: + os.replace(marker, prefix / f'.init.pid.stale-{pid}-{time.time_ns()}') + print(f'Recovered stale Darling server marker for PID {pid}', flush=True) + else: + server_running = True + except (OSError, ValueError): + pass + +# Only clean up stale Riot temporary files and shared memory frames if the server is NOT running +if not server_running: + try: + tmp_dir = prefix / 'private/tmp' + if tmp_dir.is_dir(): + for f in tmp_dir.glob('.com.riotgames.*'): + try: + f.unlink(missing_ok=True) + except OSError: + pass + except OSError: + pass + + try: + shm_dir = Path('/dev/shm') + if shm_dir.is_dir(): + for f in shm_dir.glob('darling-*'): + try: + f.unlink(missing_ok=True) + except OSError: + pass + except OSError: + pass diff --git a/tools/m58f299-xshm.c b/tools/m58f299-xshm.c index c275b47..2e1324f 100644 --- a/tools/m58f299-xshm.c +++ b/tools/m58f299-xshm.c @@ -135,10 +135,8 @@ static int drawFrame(Frame *f, Drawable target, GC gc, const void *pixels, (const char *)pixels + y * stride, f->width * 4); int result = XShmPutImage(f->display, target, f->privateGC, f->image, 0, 0, 0, 0, f->width, f->height, False); - /* darling-lol perf: XFlush is non-blocking - sends the request without waiting - * for the server round-trip. The SHM segment is reused on the next tick anyway, - * and the server processes requests in order. Saves 0.2-1.0ms per frame. */ - XFlush(f->display); + /* Wait for server consumption before reusing the segment on the next tick. */ + XSync(f->display, False); pthread_mutex_unlock(&f->render); return result; } @@ -152,7 +150,7 @@ DarlingXShmPut(void *handle, Drawable target, GC gc, const void *pixels, pthread_mutex_lock(&f->render); int result = XShmPutImage(f->display, target, f->privateGC, f->image, 0, 0, 0, 0, f->width, f->height, False); - XFlush(f->display); + XSync(f->display, False); pthread_mutex_unlock(&f->render); return result; } diff --git a/tools/riot-desktop-entry.py b/tools/riot-desktop-entry.py index c4c1dd8..6782c31 100755 --- a/tools/riot-desktop-entry.py +++ b/tools/riot-desktop-entry.py @@ -147,7 +147,7 @@ Type=Application Name=League of Legends GenericName=MOBA Game Comment=Play League of Legends on Linux with darling-lol -Exec={run_script} +Exec="{run_script}" Path={PROJECT_ROOT} Icon=league-of-legends Terminal=false