mirror of
https://github.com/lucannez64/darling-lol.git
synced 2026-10-26 19:47:42 +00:00
Fix Darling runtime performance and new-PC compatibility
Preserve floating-point arguments in GL resolvers, retain presentation across differently sized windows, export AppKit click and URL fixes, and guard nullable LaunchServices errors. Include compiler/build and installer corrections for the new PC. Reuse server receive buffers with descriptor-safe exception cleanup and avoid disabled debug-string allocation. Translate Darwin free/reuse memory hints, add atomic server/library installers with separate user-owned memory backups, and preserve active processes until a manual namespace restart. Remove automatic inspectors, client helpers and forced unlimited UI frames from normal launches. Retain historical hooks with offline mocks and document their exclusion, unresolved installer handoff, activation requirements and unmeasured FPS/RAM benefits. Validation: all 29 pinned patches apply; all 461 exported source files match; host receive and memory checks, launcher and JavaScript mocks, syntax and documentation-link checks pass. No live client/game was controlled or restarted.
This commit is contained in:
1 parent
a96cdc62b1
commit
5609d96f16
31 files changed
+1530
-187
No files matched your search
+44
-2
@@ -16,8 +16,50 @@ The normal wrappers currently support the default `~/.darling` prefix and `/usr/
|
||||
|
||||
The build uses Unix Makefiles because the incremental runtime helpers read CMake `flags.make` and `link.txt`. Ninja is not a drop-in replacement for those helpers.
|
||||
|
||||
The supported build is x86-64 only (`TARGET_i386=OFF`). The patched OpenDirectory uses Objective-C ivars that the 32-bit fragile ABI cannot compile. Keep the empty `CMAKE_BUILD_TYPE` used by `scripts/build.sh`: selecting `Release` introduces `NDEBUG` while objc4 defines `OBJC_IS_DEBUG_BUILD=1`, causing a debug-macro mismatch. The base patch requests the macOS 10.15 Objective-C runtime code generation to avoid undefined `__NSArray0__struct` / `__NSDictionary0__struct` symbols with newer Clang. Indium handles both the LLVM 22 and LLVM 23 branch opcode names.
|
||||
|
||||
Linux 5.14 or newer is required for `MADV_POPULATE_WRITE`, used when Darwin's allocator reuses lazily reclaimable pages. The memory installer checks this requirement; do not bypass it.
|
||||
|
||||
The runtime installer requires native `pkg-config` entries for `vpx`, `x11`, `x11-xcb`, `xcb-shm` and `xext`, plus the base Darling SDK. Some bridge guards intentionally fall back to Chromium's own implementation when the installed client changes.
|
||||
|
||||
The launcher enables loopback-only Electron inspection on `127.0.0.1:9229` for its startup compatibility hooks. It reads local Riot API credentials from the running client when necessary; no credentials are stored in this repository. Keep logs and local captures private.
|
||||
Run configuration, builds and the per-user runtime installation as your ordinary account. Use sudo only for the base CMake installation and the incremental server installer. Running the whole build or per-user install under sudo can create root-owned files and configure the wrong guest home. `scripts/install-runtime.sh` initializes the default user prefix and creates `~/.darling/private/tmp` with mode `1777`, so the installer can write guest temporary files. It then shuts down Darling before replacing the paired runtime: first exit the game and both clients manually.
|
||||
|
||||
Build/install steps need substantial time and storage. The patch set was checked against all pinned base indexes on the development machine. An entirely fresh build and a game launch on the second PC remain to be tested; do not interpret patch applicability as proof that every distro/driver works.
|
||||
The normal launcher starts neither an Electron inspector nor client API/UI helpers. It retains the existing runtime libraries and compatibility shims, disables incompatible Chromium features, supplies an absolute log directory, and selects the League product. The first-time installer uses `--skip-to-install` and omits the installed client's `--app-root`, avoiding an immediate exit or inaccessible intro screen. Legacy inspector hooks, API helpers, input automation and client binary patches remain historical tools; do not use them in the manual workflow. Keep account data, logs and captures private.
|
||||
|
||||
## Updating an existing runtime
|
||||
|
||||
These commands assume the pinned, patched source tree and its configured Makefiles already exist. From the repository root, build the two components with one worker to reduce resource contention:
|
||||
|
||||
```sh
|
||||
cmake --build build/darling-src/build --target darlingserver system_kernel --parallel 1
|
||||
sudo python3 tools/install-riot-darling-server.py
|
||||
python3 tools/install-riot-memory-runtime.py
|
||||
```
|
||||
|
||||
These two Python installers derive paths from the checkout/configuration and may be run directly from `tools/`; tokenized framework builders still require their rendered copies. The server installer defaults to `/usr/local/bin/darlingserver`, saves a checksum-named backup under `build/backups/darlingserver`, and preserves active server mappings. The memory installer updates the guest `RuntimeLibraries` and per-prefix bootstrap copy of `libsystem_kernel.dylib`, plus `build/riot-libsystem_kernel.dylib` if present. It leaves the system SDK alone. Each replacement is staged and verified before installation; the memory installer prepares every destination before replacing any. Replacement is atomic per file, not a transaction across all files.
|
||||
|
||||
Memory backups default to **`build/memory-runtime-backups`**, separate from the potentially root-owned directory created by the sudo server installer. If a custom backup directory is unwritable, choose another with `--backup-dir /path/to/writable/backups`. See [permission troubleshooting](TROUBLESHOOTING.md#memory-installer-permission-denied). Neither installer stops or starts any process.
|
||||
|
||||
An on-disk update is not activation. Existing processes keep their old executable/library mappings, and Darling's active overlay can expose an old library even to a new guest process. After manually exiting the game and both Riot/League clients, restart the entire namespace:
|
||||
|
||||
```sh
|
||||
darling shutdown
|
||||
sh scripts/run.sh
|
||||
```
|
||||
|
||||
`darling shutdown` terminates the guest namespace; never run it during a match or while applications have work to save. Reopening only the client is insufficient. `scripts/run.sh` renders the current launcher templates for the next launch. No FPS/RAM improvement has yet been established for these incremental changes; see [status](STATUS.md) and [standalone checks](TESTING.md).
|
||||
|
||||
For GL resolvers, multi-window presentation and the LaunchServices crash fix, use the full paired installation from the README after exiting clients. It rebuilds the native GL wrapper and installs both GL library copies, as well as the LaunchServices absolute subframework path used by Electron. Copying only the OpenGL framework or only the flat LaunchServices override leaves an old library reachable.
|
||||
|
||||
Build/install steps need substantial time and storage. All 29 component patches were checked against their pinned base indexes. The runtime has been exercised on the new RX 6600 PC, but an entirely fresh checkout/build/install remains to be validated on another machine; patch applicability alone does not establish distro/driver support. Use `JOBS=2 ./scripts/build.sh` if the default `nproc` parallelism exceeds your available RAM. See [troubleshooting](TROUBLESHOOTING.md) before changing compiler flags or prefix ownership.
|
||||
|
||||
## Committing from a new PC
|
||||
|
||||
Building does not require a Git author identity, but committing does. If Git reports `Author identity unknown`, configure your own name and email in this repository (use your GitHub noreply address if desired):
|
||||
|
||||
```sh
|
||||
git config user.name "Your Name"
|
||||
git config user.email "your-commit-email"
|
||||
```
|
||||
|
||||
Omitting `--global` keeps these settings local to this checkout. Commit source patches, tools and docs; keep `build/`, binaries, backups, captures and account data ignored.
|
||||
+41
-7
@@ -1,13 +1,47 @@
|
||||
# Snapshot status — 2026-10-12
|
||||
# Snapshot status — 2026-10-13
|
||||
|
||||
This source snapshot includes current local modifications, including experiments that have not all been compared against a fresh clean build. It preserves a development runtime, not an upstream-ready patch series.
|
||||
This repository preserves a development runtime, including historical experiments. All 29 patches apply to their pinned revisions and the exported source matches the development tree for the patched files. A fresh checkout/build/install has not been validated end to end on another machine. The normal workflow uses manual client installation and play, without debugger attachment, client API calls or input automation.
|
||||
|
||||
Confirmed fixes include a lost wake in kernel workqueue semaphore handling, callback-safe CoreAudio stop, AppKit event handling/post-game controls, and delivering ready main-queue callbacks on every event poll while keeping expensive CF run-loop work throttled.
|
||||
## New PC: verified runtime changes
|
||||
|
||||
The presentation callback regression changed from 180 callbacks during 200 polls to 200 callbacks with zero late polls. Native pixel and input checks passed. The user subsequently reported no visible stutter, but still reported sharp fight FPS drops.
|
||||
The current machine uses a Ryzen 5 5500, RX 6600 and Linux 7.2.9. The source snapshot includes the build corrections needed with its newer Clang/LLVM: x86-64-only OpenDirectory, consistent objc4 debug macros, older Objective-C collection code generation and LLVM 22/23 branch opcode compatibility.
|
||||
|
||||
A separate presentation defect exported a GPU synchronization FD on the deferred worker while the GL context belonged to the main thread. The live game accumulated over 45,000 syncobj FDs. The corrected import runs on the compositor thread; native stress leaked zero handles over 120 frames, versus 120 on the old build. The installed game was checked at zero leaked syncobj handles and about 111 total open files.
|
||||
- **Video scaling:** generated ELF GL resolvers lost floating-point arguments on the first call. Chromium's first linear sampler request failed, leaving nearest sampling. Preserving registers fixes the native first-call sampler/color probe; the old wrapper fails both checks. An 800×450 checkerboard scaled to 640×360 changes to bilinear sampling.
|
||||
- **Startup presentation:** drawing another differently sized surface in the same GL context stopped the active async presenter. Those surfaces now fall back to readback without stopping it. The old build fails after three visible frames; the corrected native probe retains the presenter through 357 fallback frames and presents all 360 frames at about 60 FPS. User confirmation of the splash remains pending.
|
||||
- **UI clicks:** window dragging now examines the frontmost view by geometry, including Electron's control-region overlays that deliberately return nil from hit testing. Previously a click could reach the underlying draggable web view and start a drag instead of activating a control.
|
||||
- **External-link crashes:** six LaunchServices paths dereferenced nullable error-output pointers. They now check the pointers and populate supplied outputs. AppKit also implements the missing URL-opening configuration/completion methods. Native nullable-output checks passed. The paired installer covers Electron's absolute LaunchServices subframework path as well as the flat override.
|
||||
- **Installation:** the default prefix gets a writable `/private/tmp`; the first-time launcher skips the inaccessible native intro and avoids an installed-client `--app-root`. The fetcher accepts missing informational `Info.plist` metadata. A separate HTTP 409 LaunchAgent handoff failure remains unresolved in the manual launch path.
|
||||
|
||||
A post-fix, limited-ability Baron run measured 99.6 FPS average and 16.11 ms p99 (62.1 reciprocal FPS). Another spell-heavy attempt measured 85.7 FPS average and 23.51 ms p99 (42.5 reciprocal FPS); practice refresh/camera differences prevent treating these as a clean improvement comparison. These are short practice measurements, not an ARAM guarantee or a 90 FPS p99 result. Historical comparisons using a reset helper that alternated refresh toggles are unreliable.
|
||||
The historical update hook handles HTTP 409, interrupted `pending_verify` and active downloads in 28 mocked scenarios. Historical external-link forwarding was exercised before the workflow changed. Both inspector-based helpers are now disabled in normal launches. Native URL methods can queue requests, but no normal host spool watcher consumes them; host-browser forwarding is not established by the crash fix. Legacy Electron visibility patches and automated practice/profiling tools are excluded from recommended setup/testing.
|
||||
|
||||
The development system uses an AMD Barcelo integrated GPU, RADV, Xwayland/Hyprland and two 60 Hz displays without active VRR. Driver capabilities, CPU power sharing and display refresh can differ on another PC. No CPU-affinity or compositor configuration is installed automatically: the legacy `pin-game.sh` assumes this laptop's CPUs and must be adapted before use elsewhere.
|
||||
## FPS observation and server optimization
|
||||
|
||||
An 18-minute host observation found substantial Darling request overhead and repeated main-thread socket waits consistent with its RPC path. The user reported 100–120 FPS in quieter moments and 70–80 during fights, versus 170–190 on Windows. GPU utilization averaged about 61% with an 85% maximum. CPU temperatures were 92–94°C, with reported clocks averaging 4.13 GHz. Background clients remained busy; the server still used about 1.5 logical CPUs after the observed game exited.
|
||||
|
||||
Server-only CPU samples identified worker context switching and receive/message allocation as substantial costs. These observations do not establish the entire FPS gap, distinguish every intentional game wait from added RPC latency, or prove a per-frame cause.
|
||||
|
||||
The receive queue now retains unused receive slots, transfers real messages into the existing queue and adopts every returned control header before potentially throwing. This preserves received descriptor ownership even if deque allocation fails. Disabled debug logging no longer prepares PID/TID strings. Independent host socket checks reduce warmed empty receive allocations from 32 to zero and single-packet allocations from 64 to two. Normal and address/undefined-behavior sanitizer checks passed, including exception cleanup and descriptor ownership.
|
||||
|
||||
The normal launcher no longer enables a client inspector, starts scheduling/update/link helpers or forces unlimited UI frames. The user installed the new server atomically while the old server remained active. **No post-activation FPS gain has been measured.** Validate through manually played matches after a full Darling restart when the game and clients are closed.
|
||||
|
||||
## Memory reclamation and activation
|
||||
|
||||
A memory audit attributed roughly 4.3 GiB of proportional resident memory to the clients/runtime, with 50 crash-handler processes present. About 3 GiB was private anonymous memory/native heap, and graphics mappings accounted for about 238 MiB. A five-minute idle observation showed no rapid growth (Riot changed about 1 MiB, League about 10 MiB). All observed processes reported zero `LazyFree` memory.
|
||||
|
||||
Installed-library disassembly confirmed that Darwin `MADV_FREE`, `MADV_FREE_REUSABLE` and `MADV_FREE_REUSE` were successful no-ops. Apple malloc uses the free/reusable hints for freed page ranges. The patch maps free/reusable to Linux `MADV_FREE` and reuse to `MADV_POPULATE_WRITE`, cancelling lazy reclamation without changing surviving contents. Linux 5.14+ is required.
|
||||
|
||||
The kernel library built; host and independent guest probes marked all 4 MiB of their own buffer reclaimable and cancelled that state on reuse while preserving contents. The old implementation marked zero pages. Exported symbols and library dependencies remained unchanged. This establishes the reclamation fix, not the fraction of the clients' footprint it will recover. Lazy reclamation may leave RSS unchanged while RAM is available.
|
||||
|
||||
The memory installer initially failed because the sudo server installation had made `build/backups` root-owned. Its default is now user-owned `build/memory-runtime-backups`, and it stages every replacement before changing any destination. The corrected installer replaced the runtime library, per-prefix bootstrap copy and prepared artifact without stopping processes; all 75 existing process mappings retained their old inode.
|
||||
|
||||
A fresh standalone guest probe still saw the old implementation: the active overlay exposed the old library despite the host file having the new checksum. **The fixes are installed on disk and require a full namespace restart to activate.** After manually exiting the game and both clients, run `darling shutdown`, then `sh scripts/run.sh`. Neither installer performs that restart. Real-client RAM improvement remains unmeasured. See [setup](SETUP.md) and [troubleshooting](TROUBLESHOOTING.md).
|
||||
|
||||
## Earlier laptop results (historical)
|
||||
|
||||
The earlier AMD Barcelo laptop used RADV, Xwayland/Hyprland and two 60 Hz displays without active VRR. Results from its historical automated practice experiments are not current validation and those workflows are no longer recommended.
|
||||
|
||||
Confirmed earlier fixes include a lost wake in kernel workqueue semaphore handling, callback-safe CoreAudio stop, AppKit event handling/post-game controls and delivery of ready main-queue callbacks on every event poll while throttling expensive CF run-loop work. The presentation callback regression improved from 180 callbacks during 200 polls to 200 with zero late polls. Native pixel and input checks passed.
|
||||
|
||||
A deferred compositor path used to export one GPU synchronization FD per frame and lose ownership. Scoped ownership reduced a 120-frame native probe from 120 leaked FDs to zero; extended compositor probes also remained flat. A later practice capture had higher GPU utilization and flatter presentation intervals, without isolating a clean before/after performance comparison.
|
||||
|
||||
Historical short Baron runs measured 99.6 FPS average / 16.11 ms p99 and 85.7 FPS average / 23.51 ms p99 in a spell-heavy attempt. Refresh/camera differences and old reset helpers make those comparisons unreliable. They do not demonstrate a 90 FPS p99 result or an ARAM guarantee. Laptop-specific CPU affinity is not installed automatically; do not use `pin-game.sh` unchanged on another CPU.
|
||||
+68
-6
@@ -1,5 +1,22 @@
|
||||
# Testing on another PC
|
||||
|
||||
Run standalone probes and offline mocks; launch and play matches manually. Do not attach a debugger, query client APIs, inject diagnostic client/game hooks or automate input. Do not stop a running client/game to test a runtime change. Presentation probes should use their own windows with clients closed, since the native presentation fixtures use shared per-user target names.
|
||||
|
||||
## Patch and offline checks
|
||||
|
||||
After preparing the pinned source tree:
|
||||
|
||||
```sh
|
||||
python3 scripts/prepare.py --check-only
|
||||
python3 tools/riot-launcher-offline-check.py
|
||||
node tools/riot-league-update-check.js
|
||||
node tools/riot-open-url-check.js
|
||||
```
|
||||
|
||||
The patch check uses temporary base indexes and does not reset or modify the source checkout. The launcher check uses stub commands and never starts or contacts Riot or Darling. The JavaScript checks use mock Electron/filesystem/HTTP objects; they do not enable the legacy hooks in a real client. The installer handoff mock covers 28 scenarios, including HTTP 409, interrupted verification and preserving active downloads.
|
||||
|
||||
## Standalone guest probes
|
||||
|
||||
Build and install the base/runtime first. Native probes are built by the incremental runtime scripts. With the default guest username, run a probe like this (replace `me` and the host checkout path):
|
||||
|
||||
```sh
|
||||
@@ -15,21 +32,66 @@ Build additional probes when needed, for example:
|
||||
python3 build/runtime/tools/build-riot-metal-concurrency-check.py
|
||||
```
|
||||
|
||||
Useful probes:
|
||||
Useful probes (build tokenized Python helpers from `build/runtime/tools/`):
|
||||
|
||||
- `riot-launchservices-null-error-check`: nullable LaunchServices error outputs must not crash, and a supplied empty output must receive an error. Build with `build-riot-launchservices-check.py`; expected `nullable_outputs=1 error_populated=1 missing_handlers=1`.
|
||||
- `riot-appkit-pump-check`: every queued callback must arrive in its own event poll; expected `events=200 callbacks=200 late_polls=0`.
|
||||
- `riot-event-coalescing-check`: preserve key events and latest mouse motion.
|
||||
- `m327-cgl-resolver-check`: first calls through dynamically looked-up GL functions retain floating-point arguments; expected `first_sampler_linear=1 first_color_arguments=1 errors=0,0`. Build with `build-m58f327-opengl.py` and run with `run-riot-native-check.py m327-cgl-resolver-check`.
|
||||
- `m327-cgl-direct-check`: with `RCLOL_GPU_DIRECT_OTHER_WINDOW_TEST=1` and an owned 337x339 native target, interleave 338x339 fallback surfaces; expected `active_presenter_preserved=1 fallback_frames=357`. This needs the native presentation fixture; it is not a `run-riot-native-check.py` choice.
|
||||
- `riot-metal-presentation-check`: Vulkan clear reaches the GL texture, including late window/context attachment.
|
||||
- `riot-metal-concurrency-check`: submission ordering, resource lifetime and concurrent recording.
|
||||
|
||||
`RIOT_PRESENTATION_STRESS=1` makes the presentation probe run 120 frames. Force async encoding for a non-game probe with the guest flag `/tmp/metal-async-encode`, then remove it after testing. The game enables async encoding by default. The tested old path leaked 120 synchronization FDs over 120 frames; the fixed path leaked zero. Inspect the host process's `/proc/PID/fd` for `anon_inode:syncobj_file` if reproducing that test.
|
||||
`RIOT_PRESENTATION_STRESS=1` makes the presentation probe run 120 frames. With the game and clients closed, force async encoding for this standalone probe using the guest flag `/tmp/metal-async-encode`, then remove it. The tested old path leaked 120 synchronization FDs over 120 frames; the fixed path leaked zero. Inspect the probe's host `/proc/PID/fd` for `anon_inode:syncobj_file` if reproducing that test.
|
||||
|
||||
For gameplay, start a practice game through Riot. Check sound, keyboard/mouse buttons, fullscreen confinement, camera motion and returning to the client. Use the real UI; an unknown version/layout is a reason to stop automation and inspect it.
|
||||
## Host receive queue regression
|
||||
|
||||
The rendered `baron-setup.py` is for a fresh practice game. `baron-reset.py` is for an already-configured scene and deliberately preserves refresh toggles. The helpers expect a focused 1920x1080 game at global 0,0 and identify the two verified panel sizes before clicking. Auto-refresh settings are toggles; visually verify them rather than blindly repeating setup on an existing match.
|
||||
The receive-buffer regression runs entirely on independent host sockets:
|
||||
|
||||
Passive captures are opt-in. `CYCLE_FRAME_INTERVALS=1 sh build/runtime/tools/profile-cycle.sh` restarts the runtime and creates/reconnects to practice; do not run it during a real match. It enables bounded timing blocks after a 90-second warmup. Native capture files under the guest `/tmp` contain request/render/flush timestamps, not physical scanout times. Re-arm through the guest `/tmp/metal-frame-interval-repeat` flag. The current bounded recorder writes a block synchronously; its file-write boundary can distort isolated worst frames. Treat it as diagnostic instrumentation.
|
||||
```sh
|
||||
c++ -std=c++17 -O2 -pthread \
|
||||
-I build/darling-src/src/external/darlingserver/internal-include \
|
||||
tools/riot-host-receive-check.cpp \
|
||||
build/darling-src/src/external/darlingserver/src/message.cpp \
|
||||
-Wl,--wrap=malloc -o build/riot-host-receive-check
|
||||
build/riot-host-receive-check
|
||||
python3 tools/riot-launcher-offline-check.py
|
||||
```
|
||||
|
||||
Compare the same scene, ability activity and camera behavior with the correct charger connected. Do not report a paused, dead, unfocused or empty scene as a fight benchmark. Report p99 frame time as milliseconds; the reciprocal is a percentile-equivalent FPS, not the mean of the slowest 1% of frames.
|
||||
Expected: `warm_empty_allocations=0 single_packet_allocations=2`, correct credentials, descriptor ownership/CLOEXEC, sender addresses, multiple batches, truncation flags, callback reentrancy and exception cleanup. The old receive implementation makes 32 allocations for a warmed empty receive and 64 for a single-packet receive in the same fixture. These are allocation counts, not an FPS benchmark. The launcher check uses stub commands and never starts or contacts Riot or Darling.
|
||||
|
||||
The same source passed AddressSanitizer/UndefinedBehaviorSanitizer and leak detection. Allocation counts depend on this fixture/compiler/library; preserve the no-wasted-slot property rather than interpreting them as game performance. See [setup](SETUP.md#updating-an-existing-runtime) for server build/install/activation.
|
||||
|
||||
## Memory reclamation regression
|
||||
|
||||
The memory-reclamation check links the actual Darling `sys_madvise` implementation to host syscall hooks and uses only its own 4 MiB anonymous allocation:
|
||||
|
||||
```sh
|
||||
python3 tools/build-riot-host-madvise-check.py
|
||||
build/logs/madvise-fix/host-madvise-check
|
||||
```
|
||||
|
||||
Expected: `reclaimable_KiB=4096 reuse_reclaimable_KiB=0 contents=preserved`. The old implementation returns success without marking any pages reclaimable. Darwin `MADV_FREE` and `MADV_FREE_REUSABLE` now map to Linux `MADV_FREE`; Darwin `MADV_FREE_REUSE` uses `MADV_POPULATE_WRITE` to cancel lazy reclamation while preserving surviving contents. The reuse operation requires Linux 5.14 or newer; an unsupported or invalid operation returns the translated kernel error rather than reporting false success. Reclamation is lazy and does not promise an immediate RSS decrease when RAM is available.
|
||||
|
||||
For the actual guest ABI, `tools/build-riot-madvise-check.py` builds an independent executable. After a full restart has activated the installed library, run it as follows; replace `me` and the checkout path:
|
||||
|
||||
```sh
|
||||
python3 tools/build-riot-madvise-check.py
|
||||
darling shell env \
|
||||
DYLD_LIBRARY_PATH=/Users/me/RuntimeLibraries \
|
||||
/Volumes/SystemRoot/path/to/darling-lol/build/logs/madvise-fix/riot-madvise-check
|
||||
```
|
||||
|
||||
Expected: `DARLING_MADVISE pass=1 reclaimable_KiB=4096 reuse_reclaimable_KiB=0 contents=preserved`. To test a candidate before installation, place only its `libsystem_kernel.dylib` in a private directory and prepend its guest-visible path to `DYLD_LIBRARY_PATH`. The candidate passed; the previous installed implementation failed with zero reclaimable pages. The probe uses only its own allocation and Linux memory counters, and never contacts a client/game. It tests free/reuse hints directly, not complete Apple malloc or client behavior under memory pressure.
|
||||
|
||||
Install the tested library with `python3 tools/install-riot-memory-runtime.py`. It uses user-owned `build/memory-runtime-backups` instead of the shared `build/backups` directory, which an earlier sudo server install may have made root-owned. It atomically replaces the two per-user kernel-library copies and the prepared runtime artifact, without stopping any process or changing the system SDK. The active Darling overlay can retain the old file even for new guest processes after an update to its backing directory; this was verified by differing host versus namespace checksums. After exiting the game and both clients manually, run `darling shutdown`, then `sh scripts/run.sh` to start a new namespace. Do not restart Darling during a match. Verify reclamation and client memory use after that restart.
|
||||
|
||||
## Manual game observation
|
||||
|
||||
Play matches manually through Riot. Check sound, keyboard/mouse buttons, fullscreen confinement, camera motion and returning to the client. The legacy `baron-setup.py`, `baron-reset.py`, `profile-cycle.sh`, inspector scheduling/update/URL helpers and Electron binary patching tools are excluded from this workflow. Normal launches enable none of these helpers and do not force unlimited UI frames. Automatic installer handoff and host external-link forwarding are consequently unavailable.
|
||||
|
||||
Observe host CPU/thread scheduling, GPU counters, temperatures and memory pressure without controlling the client/game. Record manual FPS readings with timestamps and comparable graphics settings. Do not interpret a socket wait as avoidable RPC latency without distinguishing intentional synchronization. Server-only CPU sampling identifies costs but does not measure per-frame causality.
|
||||
|
||||
Compare similar scene, ability and camera activity with the same power settings (and correct charger on a laptop). Do not report a paused, dead, unfocused or empty scene as a fight benchmark. If an allowed host measurement supplies frame times, report p99 in milliseconds; its reciprocal is percentile-equivalent FPS, not the mean of the slowest 1% of frames. Manual FPS readings alone do not establish p99 frame times.
|
||||
|
||||
The CPU symbolizer offers `--leaf-only` because the host DWARF unwinder can drop guest Mach-O stacks. Successfully unwound samples alone are not a representative profile. Local API lockfiles, account data and raw logs must stay outside version control.
|
||||
@@ -0,0 +1,83 @@
|
||||
# Troubleshooting
|
||||
|
||||
Use the [setup instructions](SETUP.md) from the repository root as your ordinary user. Exit the game and both clients manually before a full installation or Darling restart. Incremental server/memory installers can replace files without interrupting processes, but their changes need a later full restart to activate.
|
||||
|
||||
## Memory installer: permission denied
|
||||
|
||||
An earlier sudo server installation can create root-owned `build/backups`. The old memory installer tried to create `build/backups/memory-runtime` there and failed at `backup_folder.mkdir(...)`. The corrected default is a separate user-owned `build/memory-runtime-backups` directory:
|
||||
|
||||
```sh
|
||||
python3 tools/install-riot-memory-runtime.py
|
||||
```
|
||||
|
||||
For an explicitly chosen backup location:
|
||||
|
||||
```sh
|
||||
python3 tools/install-riot-memory-runtime.py --backup-dir "$HOME/darling-memory-backups"
|
||||
```
|
||||
|
||||
If the destination library itself is root-owned, inspect the named path and its ownership before retrying with sudo. Do not run all setup/build steps as root or recursively change ownership of the system SDK. Backups and binaries are ignored by Git and must stay local.
|
||||
|
||||
## Updated library or server still behaves like the old version
|
||||
|
||||
Atomic replacement preserves active process mappings. Darling also uses an overlay filesystem; updating its backing prefix from the host can leave an old file visible inside the active namespace. A fresh standalone guest check continued to report the old memory behavior despite the host path having the new checksum. Reopening only Riot does not replace this namespace.
|
||||
|
||||
After exiting the game and both clients manually:
|
||||
|
||||
```sh
|
||||
darling shutdown
|
||||
sh scripts/run.sh
|
||||
```
|
||||
|
||||
Do not run these commands during a match. Use the independent reclamation probe in [testing](TESTING.md) to verify the new guest behavior after activation. An installer result with `installed: true` and `restart_performed: false` confirms the disk update, not activation.
|
||||
|
||||
## Around 5 GB of RAM with just the clients open
|
||||
|
||||
The observed proportional resident footprint was roughly 4.3 GiB across Riot, League and Darling, including 50 crash-handler processes. Roughly 3 GiB came from private anonymous mappings/native heaps; graphics mappings accounted for about 238 MiB. Large virtual address reservations are not equivalent to resident RAM, and summed RSS double-counts shared pages. Use proportional set size (PSS), swap and host memory pressure to assess the actual footprint.
|
||||
|
||||
The old kernel library returned success for Darwin allocator free/reuse hints without making any pages reclaimable. The replacement maps free/reusable hints to Linux `MADV_FREE`, and reuse to `MADV_POPULATE_WRITE`. The standalone 4 MiB buffer becomes reclaimable and returns to normal on reuse while preserving surviving data. This fixes a demonstrated runtime behavior, but does not explain all client allocations or prove a specific RAM reduction. Linux may retain lazily reclaimable pages in RSS while RAM is available. A five-minute idle observation did not show rapid client memory growth.
|
||||
|
||||
Build/install the kernel library using [the incremental update steps](SETUP.md#updating-an-existing-runtime), then perform the manual restart when finished playing. Linux 5.14+ is required. Do not kill client helpers as an attempted memory optimization during play.
|
||||
|
||||
## Installer exits, cannot write temporary files, or Install appears idle
|
||||
|
||||
Use `sh scripts/installer.sh euw` (or your supported region). The first-time launcher must omit the installed client's `--app-root` and pass `--skip-to-install`; the normal wrapper now does this. It also selects `league_of_legends` so the product installation UI appears after login.
|
||||
|
||||
If logs report guest `/private/tmp` permission errors, the default prefix needs its own temporary directory. The corrected full runtime installer creates it. To repair just this default-prefix directory as your ordinary user:
|
||||
|
||||
```sh
|
||||
mkdir -p "$HOME/.darling/private/tmp"
|
||||
chmod 1777 "$HOME/.darling/private/tmp"
|
||||
```
|
||||
|
||||
The installer fetcher no longer treats a missing archive `Info.plist` or the informational `unzip | head` listing as a fatal download failure. Download and extraction errors still fail. The wrapper invokes this fetcher with Bash, since its `pipefail` setting is unsupported by some `/bin/sh` implementations.
|
||||
|
||||
There is also a separate known compatibility gap: native LaunchAgent watch registration can return HTTP 409 after the League install path is accepted, leaving the download idle. A historical inspector hook handles this and interrupted `pending_verify` states in 28 mock cases, but that hook is disabled in normal launches and excluded from the manual workflow. There is no verified runtime-only replacement for that handoff in this snapshot. Do not assume the native permission and launcher fixes resolve every idle Install case, or enable debugger/API automation to work around it.
|
||||
|
||||
## UI crashes after a button opens an external link
|
||||
|
||||
LaunchServices dereferenced a nullable error-output pointer in six paths. The patch checks the pointer itself and populates supplied outputs; the standalone regression covers missing handlers and null outputs. Electron opens LaunchServices through an absolute CoreServices subframework path, so the paired installer also replaces that binary in the stopped guest prefix. Updating only `RuntimeFrameworks/LaunchServices.framework` can leave the crash unfixed.
|
||||
|
||||
Historical host-browser forwarding depended on an inspector hook and is disabled in normal launches. The native null-pointer fix prevents this demonstrated crash; it does not guarantee that every external link opens on the host or explain every unrelated button crash.
|
||||
|
||||
## Blocky video or flickering startup logo
|
||||
|
||||
The first dynamically resolved GL call could lose floating-point arguments during ELF symbol lookup. Chromium's linear sampler request then failed and left nearest sampling. The generated resolver now preserves those registers. Install both the rebuilt native `libGL.dylib` in `RuntimeLibraries` and the OpenGL framework's library copy using the paired runtime installer; the framework alone is insufficient.
|
||||
|
||||
A separate multi-window path stopped the active async presenter when another differently sized surface was drawn in the same context. Such surfaces now use readback without stopping it. Native regressions cover both fixes; user confirmation of the startup splash remains pending. See [testing](TESTING.md) for the first-call sampler and interleaved-window probes.
|
||||
|
||||
## Build fails on a new compiler or runs out of memory
|
||||
|
||||
- **objc4 debug macro mismatch:** use the empty build type from `scripts/build.sh`, rather than `Release`/`NDEBUG`.
|
||||
- **OpenDirectory 32-bit ivar errors:** keep `TARGET_i386=OFF`; the supported client is x86-64.
|
||||
- **Undefined empty Objective-C collection symbols:** retain the patched macOS 10.15 Objective-C runtime code generation.
|
||||
- **LLVM branch opcode errors:** retain the Indium LLVM 22/23 compatibility patch; do not replace its branch handling with an unconditional version-specific opcode.
|
||||
- **Build process killed:** lower parallelism, for example `JOBS=2 ./scripts/build.sh` or `--parallel 1` for an incremental component build.
|
||||
- **Incremental helper cannot find `flags.make` or `link.txt`:** use Unix Makefiles, not Ninja.
|
||||
- **Prebuilt Swift file contains Git LFS pointer text:** install Git LFS and fetch the original upstream assets; pointer text is not a usable library.
|
||||
|
||||
## Lower fight FPS than Windows
|
||||
|
||||
Host observation found substantial Darling CPU/RPC costs and main-thread socket waits, with reported fight FPS of 70–80 and quieter moments at 100–120 on the Ryzen 5 5500 / RX 6600. GPU utilization averaged about 61%; the CPU reached 92–94°C. These observations identify areas to investigate, not the full cause of every slow frame.
|
||||
|
||||
The server now retains unused receive buffers and avoids preparing disabled debug strings. The host socket fixture reduces warmed empty receive allocations from 32 to zero and single-packet allocations from 64 to two. Normal launches also stop forcing unlimited UI frames and inspector scheduling hooks. An FPS gain has not yet been measured after activation. Compare manually played matches with the same graphics settings and record CPU/GPU utilization, temperature and memory pressure. Do not use legacy automated practice or profiling cycles.
|
||||
Reference in new issue
Block a user