230 Commits
Author SHA1 Message Date
Hitalo M. 12b3b03a0f Fix Riot Client crash when cancelling CFHost resolution 2026-10-16 09:09:28 +02:00
Hitalo M. 67626612ac Fix Riot Client crash when cancelling CFHost resolution 2026-10-16 09:09:28 +02:00
lucannez64andClaude Opus 5.5 908f88bad7 Allocate League's Metal drawables in VRAM
League sets framebufferOnly=NO on its CAMetalLayer, and QuartzCore read
that as needing host-visible coherent memory, so the game's final
render target and the linear image handed to OpenGL both lived in
system RAM (type 2 on the RX 6600) and crossed PCIe every frame.
framebufferOnly=NO only lets Metal sample or blit the texture: nothing
maps either image (replaceRegion aborts, getBytes is a stub, Indium
maps only Shared/Managed buffers).

Drawables now take the first allowed DEVICE_LOCAL type whatever
framebufferOnly says; the guest file /tmp/metal-no-drawable-vram
restores the old placement. metal-device.log shows drawable-vram=on and
storage=vram.

A native EGL/RADV test exported the drawable's linear image from VRAM
and imported it into GL with no errors and identical pixels. In a
practice-tool baron fight on the RX 6600 with the same champion it ran
about 20 FPS faster and the same when idle; not yet measured in an ARAM.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 02:17:51 +02:00
lucannez64andClaude Opus 5.5 197b794111 Allocate League's Metal drawables in VRAM
League sets framebufferOnly=NO on its CAMetalLayer, and QuartzCore read
that as needing host-visible coherent memory, so the game's final
render target and the linear image handed to OpenGL both lived in
system RAM (type 2 on the RX 6600) and crossed PCIe every frame.
framebufferOnly=NO only lets Metal sample or blit the texture: nothing
maps either image (replaceRegion aborts, getBytes is a stub, Indium
maps only Shared/Managed buffers).

Drawables now take the first allowed DEVICE_LOCAL type whatever
framebufferOnly says; the guest file /tmp/metal-no-drawable-vram
restores the old placement. metal-device.log shows drawable-vram=on and
storage=vram.

A native EGL/RADV test exported the drawable's linear image from VRAM
and imported it into GL with no errors and identical pixels. In a
practice-tool baron fight on the RX 6600 with the same champion it ran
about 20 FPS faster and the same when idle; not yet measured in an ARAM.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 02:17:51 +02:00
lucannez64andClaude Opus 5.5 60ebfda63f Prefer VRAM for GPU-only Metal memory and mark the GL import dedicated
Indium and QuartzCore took the first memory type a resource allowed. The
Vulkan spec orders a type with no flags before every DEVICE_LOCAL type,
and NVIDIA exposes such system-RAM types, so on NVIDIA private buffers,
textures and framebufferOnly drawables could land in system RAM. They
now take the first allowed DEVICE_LOCAL type and fall back to the old
choice; shared and managed resources are unchanged.

The drawable's exported image is a dedicated Vulkan allocation, but the
OpenGL import never set GL_DEDICATED_MEMORY_OBJECT_EXT, which
EXT_external_objects requires. Mesa ignores it; it is the suspected
cause of the black screen on NVIDIA in issue #3. The import's GL errors
were never checked (reportGLErrors is compiled out), so a failure was
silent.

/tmp/metal-device.log now lists the selected device's memory heaps and
types, the memory type chosen per resource kind (with the old choice and
changed=yes/no), and the result of the first drawable and semaphore
imports into OpenGL (later ones only on failure).

Practice match on the RX 6600: all choices changed=no, import
dedicated=yes with no GL errors, semaphore ok, match=yes. Not run on
NVIDIA.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 02:04:43 +02:00
lucannez64andClaude Opus 5.5 1c30c99e51 Prefer VRAM for GPU-only Metal memory and mark the GL import dedicated
Indium and QuartzCore took the first memory type a resource allowed. The
Vulkan spec orders a type with no flags before every DEVICE_LOCAL type,
and NVIDIA exposes such system-RAM types, so on NVIDIA private buffers,
textures and framebufferOnly drawables could land in system RAM. They
now take the first allowed DEVICE_LOCAL type and fall back to the old
choice; shared and managed resources are unchanged.

The drawable's exported image is a dedicated Vulkan allocation, but the
OpenGL import never set GL_DEDICATED_MEMORY_OBJECT_EXT, which
EXT_external_objects requires. Mesa ignores it; it is the suspected
cause of the black screen on NVIDIA in issue #3. The import's GL errors
were never checked (reportGLErrors is compiled out), so a failure was
silent.

/tmp/metal-device.log now lists the selected device's memory heaps and
types, the memory type chosen per resource kind (with the old choice and
changed=yes/no), and the result of the first drawable and semaphore
imports into OpenGL (later ones only on failure).

Practice match on the RX 6600: all choices changed=no, import
dedicated=yes with no GL errors, semaphore ok, match=yes. Not run on
NVIDIA.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 02:04:43 +02:00
lucannez64andClaude Opus 5.5 3cf599c62e Log the Metal GPU and create a Vulkan device only for it
On a hybrid laptop (issue #3) Indium took whichever Vulkan device the
loader listed first, usually the integrated GPU, and still created a
VkDevice on every other supported GPU, waking a discrete GPU it never
used. Drawables reach the presenting OpenGL context as opaque FDs, which
only work on one physical device, and nothing checked that.

Indium now appends the device list, the selected device and the reason
to guest /tmp/metal-device.log, and creates a VkDevice for the selected
device only (/tmp/metal-all-devices restores the old behaviour).
/tmp/metal-gpu or METAL_INDIUM_GPU selects a device by index or name
for diagnosis. QuartzCore logs the presenting GL renderer and compares
its device UUID with the Vulkan device's (match=yes / MISMATCH); it
queries Vulkan itself, so it needs no new libindium export.

On the RX 6600 desktop the game selected RADV, created 1 VkDevice of 2
supported (lavapipe no longer gets one) and logged match=yes in a match.
Running on NVIDIA through prime-run is documented but not validated.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 00:24:58 +02:00
lucannez64andClaude Opus 5.5 5a5a8c4022 Log the Metal GPU and create a Vulkan device only for it
On a hybrid laptop (issue #3) Indium took whichever Vulkan device the
loader listed first, usually the integrated GPU, and still created a
VkDevice on every other supported GPU, waking a discrete GPU it never
used. Drawables reach the presenting OpenGL context as opaque FDs, which
only work on one physical device, and nothing checked that.

Indium now appends the device list, the selected device and the reason
to guest /tmp/metal-device.log, and creates a VkDevice for the selected
device only (/tmp/metal-all-devices restores the old behaviour).
/tmp/metal-gpu or METAL_INDIUM_GPU selects a device by index or name
for diagnosis. QuartzCore logs the presenting GL renderer and compares
its device UUID with the Vulkan device's (match=yes / MISMATCH); it
queries Vulkan itself, so it needs no new libindium export.

On the RX 6600 desktop the game selected RADV, created 1 VkDevice of 2
supported (lavapipe no longer gets one) and logged match=yes in a match.
Running on NVIDIA through prime-run is documented but not validated.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 00:24:58 +02:00
lucannez64 c483f44ede Keep Iridium LLVM headers and runtime library on the same major 2026-10-15 23:22:15 +02:00
lucannez64 f53d605365 Keep Iridium LLVM headers and runtime library on the same major 2026-10-15 23:22:15 +02:00
lucannez64andClaude Opus 5.5 96da34a633 Add an update script that re-applies changed patches
After a pull that changed a patch, prepare.py stopped: the patch neither
applied nor reverse-applied to a tree holding its earlier version. Now
it looks for the earlier version applied in that component -- the copy
it records in build/applied-patches, or a version from this
repository's history -- reverts exactly it and applies the new one.
It never resets a repository; when nothing matches it stops and names
the repository with other edits, and it restores the earlier version
if the new one does not apply.

scripts/update.sh runs build.sh, the base install and
install-runtime.sh, and refuses to start while a Riot Client or League
process is open, since the install steps stop Darling.

Checked on a sandbox Indium checkout holding the previous patch: the
result matches a clean apply, a second run is a no-op, an unrelated
local edit survives and a conflicting one stops with the error.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 22:11:26 +02:00
lucannez64andClaude Opus 5.5 3b90c550a7 Add an update script that re-applies changed patches
After a pull that changed a patch, prepare.py stopped: the patch neither
applied nor reverse-applied to a tree holding its earlier version. Now
it looks for the earlier version applied in that component -- the copy
it records in build/applied-patches, or a version from this
repository's history -- reverts exactly it and applies the new one.
It never resets a repository; when nothing matches it stops and names
the repository with other edits, and it restores the earlier version
if the new one does not apply.

scripts/update.sh runs build.sh, the base install and
install-runtime.sh, and refuses to start while a Riot Client or League
process is open, since the install steps stop Darling.

Checked on a sandbox Indium checkout holding the previous patch: the
result matches a clean apply, a second run is a no-op, an unrelated
local edit survives and a conflicting one stops with the error.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 22:11:26 +02:00
lucannez64andClaude Opus 5.5 3e76087dbe Translate LLVM 23 vector splat constants in Iridium
LLVM 23 represents vector splats such as `<3 x float> zeroinitializer`
as vector-typed ConstantFP/ConstantInt values instead of
ConstantAggregateZero/ConstantDataVector. Iridium only handled scalar
ConstantFP/ConstantInt, so on hosts with LLVM 23 nearly every League
shader failed to translate: the r3dlog fills with "Failed to compile
shader", the loading screen hits "Material Missing Pipeline" and the
game crashes. Our matches had only run the overlay pinned to LLVM 22.

Declare the scalar element and splat it into a constant composite.
Offline, on the 74,112 metallibs in ShaderCache.metal.wad.client,
LLVM 23 went from ~2% to 74,108 translated, the same set as LLVM 22
(the 4 remaining failures are pre-existing on both); a sample of the
LLVM 23 SPIR-V passes spirv-val for Vulkan 1.3. Confirmed in a match
with the Iridium overlay built against the system LLVM 23.

RIOT_IRIDIUM_SYSTEM_LLVM=1 makes build-riot-indium-runtime.py ignore
the LLVM 22 pin, to build what a fresh checkout gets.

Refs #1

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 22:05:05 +02:00
lucannez64andClaude Opus 5.5 f5c46c6529 Translate LLVM 23 vector splat constants in Iridium
LLVM 23 represents vector splats such as `<3 x float> zeroinitializer`
as vector-typed ConstantFP/ConstantInt values instead of
ConstantAggregateZero/ConstantDataVector. Iridium only handled scalar
ConstantFP/ConstantInt, so on hosts with LLVM 23 nearly every League
shader failed to translate: the r3dlog fills with "Failed to compile
shader", the loading screen hits "Material Missing Pipeline" and the
game crashes. Our matches had only run the overlay pinned to LLVM 22.

Declare the scalar element and splat it into a constant composite.
Offline, on the 74,112 metallibs in ShaderCache.metal.wad.client,
LLVM 23 went from ~2% to 74,108 translated, the same set as LLVM 22
(the 4 remaining failures are pre-existing on both); a sample of the
LLVM 23 SPIR-V passes spirv-val for Vulkan 1.3. Confirmed in a match
with the Iridium overlay built against the system LLVM 23.

RIOT_IRIDIUM_SYSTEM_LLVM=1 makes build-riot-indium-runtime.py ignore
the LLVM 22 pin, to build what a fresh checkout gets.

Refs #1

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 22:05:05 +02:00
lucannez64 e94c3ce40f Fix League client resizing and runtime compatibility
Fix X11 presentation handoff after resizing, preserve NSView size callbacks, and correct content and backing-layer geometry. Add CoreText font enumeration for the language dropdown and rebuild/install the matching runtime overlays.

Correct CarbonCore Gestalt version queries and roll back proc_listpids for Vanguard testing. Document that VAN -101 can still recur. Include accumulated CoreAudio stream and AudioUnit corrections.

Add standalone font, version, resize and publisher-handoff regressions. Record manual confirmation of the language dropdown and window resizing; all 29 pinned patches and the source/script audit validate.
2026-10-15 18:44:43 +02:00
lucannez64 7d3905f373 Fix League client resizing and runtime compatibility
Fix X11 presentation handoff after resizing, preserve NSView size callbacks, and correct content and backing-layer geometry. Add CoreText font enumeration for the language dropdown and rebuild/install the matching runtime overlays.

Correct CarbonCore Gestalt version queries and roll back proc_listpids for Vanguard testing. Document that VAN -101 can still recur. Include accumulated CoreAudio stream and AudioUnit corrections.

Add standalone font, version, resize and publisher-handoff regressions. Record manual confirmation of the language dropdown and window resizing; all 29 pinned patches and the source/script audit validate.
2026-10-15 18:44:43 +02:00
lucannez64 cbf878ea6c Remove Riot client/game automation, inspection and capture tooling
Drop the tooling that drives, automates, inspects, profiles, captures or
patches the Riot client and League game process, plus the historical
research/tools probes. These were never part of the supported workflow and
involve synthetic input, attaching to Riot's process, driving its local API,
or patching its binaries.

Removed from tools/: the baron-* and practice helpers, uinput_click.py,
lcu-practice-game.py, check-riot-vanguard.py, the riot-league-* / riot-open-url-*
inspector hooks and API/state helpers, the riot-*-watch.py observers,
riot-match-capture/ profilers, riot-cursor-watch/, the analyze-*/symbolize-*
profilers, capture-game-perf.py, the native VPX bridge and the client
log-dir/autofill shims. Removed all of research/tools/.

The supported launch/install pipeline is unchanged: scripts/, the launcher and
ensure-* helpers, fetch-installer.sh, stop-darling-runtime.py, the
build-*-runtime / build-m58f3* runtime builders, the install-* overlays and
offline installer checks, and the standalone operator/-check regression probes
are all retained. Docs that referenced the removed tools were updated.
2026-10-15 13:00:01 +02:00
lucannez64 9e8165e056 Fix client window visibility in Darling AppKit, not by patching Electron
Electron 22.3.27's NativeWindowMac::IsVisible() compares the window's
occlusion state for exact equality with NSWindowOcclusionStateVisible:

    bool occluded = [window occlusionState] == NSWindowOcclusionStateVisible;

Apple's contract is a bitmask: an on-screen window's state *includes* that bit
(tested with &). The exact == test is inverted, so an Apple-correct value made
every ordered-in window look occluded and the client's visible/not-minimized
checks failed. The previous workaround byte-patched Riot's shipped Electron
Framework to flip that comparison.

Fix it in Darling's AppKit instead: -[NSWindow occlusionState] now also reports
the reserved 1 << 2 bit for an on-screen window. &-based readers (Electron's own
windowDidChangeOcclusionState:, Chromium) still see NSWindowOcclusionStateVisible
set, while the exact == test is false, so unpatched Electron resolves
IsVisible() correctly. No Riot binary is modified.

Delete tools/patch-riot-electron-visibility.py and its call in
install-riot-gpu-runtime.sh; sync README and docs/STATUS, which already
described the binary patch as excluded.
2026-10-15 12:46:48 +02:00
lucannez64 6085f98f64 Remove library injection from Riot Client launches 2026-10-15 12:11:19 +02:00
lucannez64 e554644d0d Implement legacy IOKit power assertions with host idle inhibitors 2026-10-15 12:05:30 +02:00
lucannez64 e2f1392369 Implement IOKit system sleep and wake notifications through logind 2026-10-15 11:37:25 +02:00
lucannez64andClaude Opus 5.5 084e003f79 Request the X11 EGL platform in iokitd
iokitd passed an Xlib Display to eglGetDisplay(), so Mesa chose the
platform from EGL_PLATFORM. Wayland sessions often set it to "wayland",
and the first darling command passes its environment to every guest
service; Mesa then treated the Display as a wl_display, iokitd crashed
in pthread_mutex_lock and launchd restarted it every 10 seconds. Use
eglGetPlatformDisplay(EGL_PLATFORM_X11_KHR, ...) as OpenGL already does.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 10:07:36 +02:00
lucannez64andClaude Opus 5.5 8f62017627 Open web and mail links on the Linux desktop from LaunchServices
LaunchServices now hands http, https and mailto URLs to the host desktop
itself instead of NSWorkspace writing them to a spool that nothing in
the normal workflow drains. It loads the host libsystemd through the ELF
loader and calls xdg-desktop-portal OpenURI on the session bus, falling
back to xdg-open in a transient systemd user unit, so the browser starts
outside Darling's namespaces. LSGetApplicationForURL reports the host
opener for these schemes because Electron checks for a handler before
opening a link. NSWorkspace goes through LSOpenCFURLRef.

Tested with a host-side D-Bus regression (79 checks) and in a fresh
guest prefix through open(1) and NSWorkspace, both reaching the portal
with the exact URL.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 09:58:59 +02:00
lucannez64andClaude Opus 5.5 c030b8ff82 Start the installed Riot Client after the first-time installer exits
Riot's installer puts the client in /Users/Shared/Riot Games and exits;
its LaunchServices hand-off to that client does not start it under
Darling, so the window just closes. The installer wrapper now launches
the installed client itself, and the runner links it to
/Applications/Riot Client.app when that link is missing.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 09:27:29 +02:00
lucannez64 7bbf9ec0e7 Fetch pinned darling-swift LFS objects from GitHub, not the dead host
The pinned src/external/swift/.lfsconfig points lfs.url at the retired
git-lfs.darlinghq.org, so a fresh `git submodule update --init --recursive`
blocks with no output right after src/external/sqlite is checked out: swift is
the next submodule in sorted order and its checkout waits on that host.

Override lfs.url to GitHub's endpoint, which still serves the objects (verified
44 objects / 16 MB, real Mach-O dylibs), and print explicit submodule progress so
the last completed submodule is not mistaken for the stalled one.
2026-10-15 01:12:47 +02:00
lucannez64andClaude Opus 5.5 4027cb9aa7 Make the Indium hand-off changes opt-in and record the Hyprland findings
Two matches with the Indium hand-off round (narrow present wait, deferred
blits, worker-side command-buffer lease, skipping CPU Vulkan devices) ran
slower than before, with more main-thread futex waiting. Each change is now
behind a guest flag file and off by default, so the default libindium matches
the round-3 build (vertex-descriptor dirty masks, cheaper resource setters).

The wait counters stay opt-in, write a "waits enabled" line when they are on
and keep their state per image. The docs explain that flag files created from
the host while Darling runs may stay invisible through the overlay mount.

Also adds a passive cursor watcher. The stuck in-game cursor is Hyprland's
direct scanout, not Darling. The first match with direct scanout off averaged
135 FPS. STATUS, TROUBLESHOOTING and README are updated with the new matches.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 00:29:19 +02:00
lucannez64andClaude Opus 5.5 6559f19fcc Add main-thread hardware counter capture and record its results
The counters (user space, counting only) show page walks cost the game's
main thread at most 3.5% of its cycles, so huge pages for its code are not
worth pursuing and for its heap are worth at most 1-3%.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 17:47:46 +02:00
lucannez64andClaude Opus 5.5 4370f375cd Cut Darling overhead in League fights and add passive match capture
Game process (ARAM Hexgate: 84 -> 127 FPS average, slowest stretches ~110):
- Local pthread mutexes, condition variables and Mach semaphores are on by
  default in the game process only, with /tmp/rclol-no-local-* opt-outs.
  The main thread's darlingserver waits fell from 1.36 to 0.10 ms per frame.
- Local semaphores: the count and the slot generation change in one
  compare-exchange, a destroy leaves a negative marker, the table is reset
  after fork, and names the table did not create go to the server.
- Metal: drop identical re-binds (keyed on the Indium object, depth-stencil
  retained), pass blit resources by reference, and stop resetting vertex
  descriptors twice.
- Core isolation of the main thread is opt-in (/tmp/rclol-core-isolation);
  it lowered FPS in its only match.
- Optimized libc/libc++/Foundation overlays, AVX2 string primitives, real CPU
  topology and their installers and offline checks.

Tools and docs:
- tools/riot-match-capture: passive /proc sampler, user-space IP profiler and
  reports, plus a comparison against the game's netstats.
- tools/riot-lsem-model and tools/riot-metal-bind-model: host models that
  extract the shipped code and check it.
- tools/install-riot-metal-pair.sh installs Metal and Indium together.
- Status, setup, testing and troubleshooting docs updated with the measured
  matches and the switch table; manifest file counts match the patches.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 16:48:34 +02:00
lucannez64 5609d96f16 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.
2026-10-13 15:09:23 +02:00
lucannez64 a96cdc62b1 Publish portable Darling runtime patch snapshot and testing tools 2026-10-12 20:15:55 +02:00