mirror of
https://github.com/lucannez64/darling-lol.git
synced 2026-10-26 19:47:42 +00:00
WIP: Low-latency performance and input latency optimizations
This commit is contained in:
1 parent
68f816deaf
commit
e4f8c67151
14 files changed
+122
-41
No files matched your search
+25
-10
@@ -2255,21 +2255,22 @@ index 5ae29bf4..358e606b 100644
|
||||
- (void) _enableDetectableAutoRepeat {
|
||||
int major = XkbMajorVersion, minor = XkbMinorVersion;
|
||||
XkbStateRec state;
|
||||
@@ -536,12 +603,52 @@ static NSDictionary *modeInfoToDictionary(const XRRModeInfo *mi, int depth) {
|
||||
@@ -536,12 +603,53 @@ static NSDictionary *modeInfoToDictionary(const XRRModeInfo *mi, int depth) {
|
||||
return &layout->layout;
|
||||
}
|
||||
|
||||
+/* darling-lol: the game asks for the pointer position, the pressed buttons and the modifier state about a thousand
|
||||
+ * times per second. Each answer was a synchronous X round trip (XQueryPointer / XkbGetState) that puts the main thread
|
||||
+ * to sleep until XWayland replies; while the mouse moves they dominated the slow frames. Reuse a reply for 8 ms, keep it
|
||||
+ * to sleep until XWayland replies; while the mouse moves they dominated the slow frames. Reuse a reply for 1 ms, keep it
|
||||
+ * current from the input events themselves (a motion event carries position and button state), and drop it on every
|
||||
+ * button, key, crossing or focus event so the next question after a state change is always asked for real. */
|
||||
+#include <mach/mach_time.h>
|
||||
+#include <poll.h>
|
||||
+static BOOL rclolFresh(uint64_t *stamp) {
|
||||
+ static mach_timebase_info_data_t tb;
|
||||
+ if (tb.denom == 0) mach_timebase_info(&tb);
|
||||
+ uint64_t now = mach_absolute_time();
|
||||
+ if (*stamp != 0 && (now - *stamp) * tb.numer / tb.denom < 8000000ULL) return YES;
|
||||
+ if (*stamp != 0 && (now - *stamp) * tb.numer / tb.denom < 1000000ULL) return YES;
|
||||
+ *stamp = now;
|
||||
+ return NO;
|
||||
+}
|
||||
@@ -2344,7 +2345,7 @@ index 5ae29bf4..358e606b 100644
|
||||
}
|
||||
|
||||
- (void) setWindow: (id) window forID: (XID) i {
|
||||
@@ -962,20 +1072,82 @@ static NSDictionary *modeInfoToDictionary(const XRRModeInfo *mi, int depth) {
|
||||
@@ -962,20 +1072,85 @@ static NSDictionary *modeInfoToDictionary(const XRRModeInfo *mi, int depth) {
|
||||
{
|
||||
#ifndef DARLING
|
||||
[[NSRunLoop currentRunLoop] addInputSource: _inputSource forMode: mode];
|
||||
@@ -2419,12 +2420,15 @@ index 5ae29bf4..358e606b 100644
|
||||
+ slice = next > now ? next - now : 0;
|
||||
+ }
|
||||
+ if (result == nil && slice > 0) {
|
||||
+ /* Keep this polling deadline independent of Mach wait timers. */
|
||||
+ struct { long seconds, nanoseconds; } delay = {
|
||||
+ 0, (long)(slice * 1000000000.0)
|
||||
+ };
|
||||
+ /* darling-lol perf: poll() the X11 connection fd instead of blind
|
||||
+ * nanosleep. This wakes within <0.1ms when an input event arrives
|
||||
+ * on the socket, eliminating up to 4ms of input latency jitter. */
|
||||
+ 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;
|
||||
+ extern long __lolsys_syscall(long, long, long, long, long, long, int);
|
||||
+ __lolsys_syscall((long)&delay, 0, 0, 0, 0, 0, 35 /* Linux nanosleep */);
|
||||
+ __lolsys_syscall((long)&pfd, 1, timeout_ms, 0, 0, 0, 7 /* Linux poll */);
|
||||
+ }
|
||||
+ }
|
||||
+ return result;
|
||||
@@ -2625,7 +2629,7 @@ index 5ae29bf4..358e606b 100644
|
||||
default:
|
||||
type = NSOtherMouseUp;
|
||||
}
|
||||
@@ -1151,11 +1430,13 @@ static NSDictionary *modeInfoToDictionary(const XRRModeInfo *mi, int depth) {
|
||||
@@ -1151,14 +1430,24 @@ static NSDictionary *modeInfoToDictionary(const XRRModeInfo *mi, int depth) {
|
||||
clickCount: clickCount
|
||||
deltaX: 0.0
|
||||
deltaY: deltaY];
|
||||
@@ -2640,6 +2644,17 @@ index 5ae29bf4..358e606b 100644
|
||||
// NSLog(@"MotionNotify, x=%d, y=%d, xroot=%d, yroot=%d\n",
|
||||
// ev->xmotion.x, ev->xmotion.y, ev->xmotion.x_root,
|
||||
// ev->xmotion.y_root);
|
||||
+ /* darling-lol perf: drain pending MotionNotify events for this
|
||||
+ * window from the X11 queue so we only process the latest position. */
|
||||
+ while (XCheckTypedWindowEvent(_display, ev->xmotion.window, MotionNotify, ev))
|
||||
+ ;
|
||||
+ rclolRootX = ev->xmotion.x_root;
|
||||
+ rclolRootY = ev->xmotion.y_root;
|
||||
+ rclolPointerState = ev->xmotion.state;
|
||||
+ rclolPointerStamp = mach_absolute_time();
|
||||
|
||||
CGPoint lastMotionPos = [window mouseLocationOutsideOfEventStream];
|
||||
pos = [window
|
||||
@@ -1195,14 +1476,29 @@ static NSDictionary *modeInfoToDictionary(const XRRModeInfo *mi, int depth) {
|
||||
|
||||
+ [self pointerMoved];
|
||||
|
||||
+16
-6
@@ -6725,7 +6725,7 @@ index ffd7fe054..a74ffc63f 100644
|
||||
|
||||
free(context);
|
||||
}
|
||||
@@ -383,9 +608,329 @@ CGLError CGLUnlockContext(CGLContextObj context) {
|
||||
@@ -383,9 +608,331 @@ CGLError CGLUnlockContext(CGLContextObj context) {
|
||||
return kCGLNoError;
|
||||
}
|
||||
|
||||
@@ -6948,12 +6948,14 @@ index ffd7fe054..a74ffc63f 100644
|
||||
+ if(__lolsys_syscall(1,(long)&now,0,0,0,0,228)==0){
|
||||
+ uint64_t current=(uint64_t)now.tv_sec*1000000000ull+now.tv_nsec;
|
||||
+ uint64_t deadline=ctx->native_next_present_ns;
|
||||
+ if(!deadline || (current>deadline && current-deadline>16666667ull))deadline=current;
|
||||
+ 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(!deadline || (current>deadline && current-deadline>paceNS))deadline=current;
|
||||
+ if(deadline>current){
|
||||
+ struct timespec wake={deadline/1000000000ull,deadline%1000000000ull};
|
||||
+ while(__lolsys_syscall(1,1,(long)&wake,0,0,0,230)==-4){}
|
||||
+ }
|
||||
+ ctx->native_next_present_ns=deadline+16666667ull;
|
||||
+ ctx->native_next_present_ns=deadline+paceNS;
|
||||
+ }
|
||||
+ }
|
||||
+ if(getenv("RCLOL_GPU_DIRECT_ASYNC")){int queued=enqueueAsyncPresent(ctx,binding.texture,binding.target,width,height);if(!queued){stopAsyncPresent(ctx);__atomic_store_n(&target->ready,0,__ATOMIC_RELEASE);}CGLReleaseContext(ctx);return queued;}
|
||||
@@ -8420,7 +8422,7 @@ diff --git a/src/frameworks/OpenGL/CGLAsyncPresent.inc b/src/frameworks/OpenGL/C
|
||||
new file mode 100644
|
||||
--- /dev/null
|
||||
+++ b/src/frameworks/OpenGL/CGLAsyncPresent.inc
|
||||
@@ -0,0 +1,190 @@
|
||||
@@ -0,0 +1,197 @@
|
||||
+/* Bounded GPU-only staging: the producer never waits for display cadence.
|
||||
+ * Three textures cover the currently displayed, pending, and writing frames.
|
||||
+ * GPU fences order both the completed producer copy and subsequent reuse. */
|
||||
@@ -8526,11 +8528,19 @@ new file mode 100644
|
||||
+ /* Native VSync owns the refresh cadence; avoid a second clock that
|
||||
+ * can miss the compositor's next refresh after a variable swap wait. */
|
||||
+ if(nativeVSync)continue;
|
||||
+ deadline+=16666667ull;
|
||||
+ /* darling-lol perf: use dynamic refresh interval from RCLOL_REFRESH_HZ
|
||||
+ * or default to uncapped (1ms minimum interval) when VSync is off. */
|
||||
+ static uint64_t refreshNS = 0;
|
||||
+ if (!refreshNS) {
|
||||
+ const char *hz = getenv("RCLOL_REFRESH_HZ");
|
||||
+ int rate = hz ? atoi(hz) : 0;
|
||||
+ refreshNS = rate > 0 ? 1000000000ull / (uint64_t)rate : 1000000ull;
|
||||
+ }
|
||||
+ deadline += refreshNS;
|
||||
+ uint64_t now=nativeMonotonicNS();
|
||||
+ /* Preserve the refresh phase across small scheduling delays. Reset only
|
||||
+ * after a full missed period, so lateness does not accumulate as drift. */
|
||||
+ if(now>deadline && now-deadline>16666667ull){__atomic_fetch_add(&target->reserved[2],1,__ATOMIC_RELAXED);deadline=now;}
|
||||
+ if(now>deadline && now-deadline>refreshNS){__atomic_fetch_add(&target->reserved[2],1,__ATOMIC_RELAXED);deadline=now;}
|
||||
+ nativeWaitUntil(deadline);
|
||||
+ }
|
||||
+ if(fbo)glDeleteFramebuffers(1,&fbo);
|
||||
|
||||
@@ -2400,10 +2400,12 @@ index 43f4a84..46a4f72 100644
|
||||
+ for (;;) {
|
||||
+ {
|
||||
+ std::unique_lock<std::mutex> lock(wakeMutex);
|
||||
+ submitBatchWake().wait_for(lock, std::chrono::milliseconds(2));
|
||||
+ /* darling-lol perf: 500µs batch window balances submission
|
||||
+ * merging with latency; the old 2ms added visible input lag. */
|
||||
+ submitBatchWake().wait_for(lock, std::chrono::microseconds(500));
|
||||
+ }
|
||||
+ std::unique_lock<std::mutex> queueLock(device->graphicsQueueMutex());
|
||||
+ if (!state.items.empty() && std::chrono::steady_clock::now() - state.since >= std::chrono::milliseconds(2)) flushSubmitBatchLocked();
|
||||
+ if (!state.items.empty() && std::chrono::steady_clock::now() - state.since >= std::chrono::microseconds(500)) flushSubmitBatchLocked();
|
||||
+ }
|
||||
+ }).detach();
|
||||
+}
|
||||
@@ -7449,7 +7451,10 @@ new file mode 100644
|
||||
+ 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
|
||||
+ static constexpr std::chrono::milliseconds Quarantine {1000};
|
||||
+ /* 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};
|
||||
+
|
||||
+ struct Block {
|
||||
+ VkDeviceMemory memory = VK_NULL_HANDLE;
|
||||
|
||||
+14
-4
@@ -5894,9 +5894,10 @@ index b998456..2783a4a 100644
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -696,6 +1613,41 @@ kern_return_t syscall_thread_switch_impl(
|
||||
@@ -696,6 +1613,48 @@ kern_return_t syscall_thread_switch_impl(
|
||||
.tv_sec = 0,
|
||||
.tv_nsec = 1000000
|
||||
- .tv_nsec = 1000000
|
||||
+ .tv_nsec = option_time > 0 ? (long)option_time * 1000 : 1000000
|
||||
};
|
||||
+
|
||||
+ /* darling-lol: opt-in trace (host file /tmp/rclol-select-trace) of who calls thread_switch and with which option:
|
||||
@@ -5933,8 +5934,17 @@ index b998456..2783a4a 100644
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
// Sleep for 1ms
|
||||
__linux_nanosleep(&tv, &tv);
|
||||
- // Sleep for 1ms
|
||||
- __linux_nanosleep(&tv, &tv);
|
||||
+ /* darling-lol perf: cooperative yield (option_time == 0) should not sleep
|
||||
+ * for a full millisecond — that is longer than many frame budgets at high
|
||||
+ * refresh rates. Use sched_yield for cooperative yields, and only sleep
|
||||
+ * when a timed wait was explicitly requested. */
|
||||
+ if (option_time == 0) {
|
||||
+ __linux_sched_yield();
|
||||
+ } else {
|
||||
+ __linux_nanosleep(&tv, &tv);
|
||||
+ }
|
||||
|
||||
diff --git a/libsyscall/CMakeLists.txt b/libsyscall/CMakeLists.txt
|
||||
index 57cebad..3014255 100755
|
||||
|
||||
+6
-1
@@ -9,7 +9,12 @@ python3 scripts/prepare.py
|
||||
# so Release's -DNDEBUG trips its "mismatch in debug-ness macros" #error.
|
||||
# x86-64 only: the patched OpenDirectory declares ivars in @implementation, which the
|
||||
# 32-bit (fragile ABI) slice cannot compile, and the macOS client is 64-bit.
|
||||
# darling-lol perf: inject -O3 and -march=native even with empty build type,
|
||||
# so the base Darling runtime is not compiled at -O0. The empty build type
|
||||
# is required by objc4's debug-ness macro check.
|
||||
cmake -S build/darling-src -B build/darling-src/build -G 'Unix Makefiles' \
|
||||
-DCMAKE_BUILD_TYPE= -DTARGET_i386=OFF -DCMAKE_INSTALL_PREFIX=/usr/local -DENABLE_METAL=ON
|
||||
-DCMAKE_BUILD_TYPE= -DTARGET_i386=OFF -DCMAKE_INSTALL_PREFIX=/usr/local -DENABLE_METAL=ON \
|
||||
-DCMAKE_C_FLAGS_INIT="-O3 -march=native -mtune=native -pipe" \
|
||||
-DCMAKE_CXX_FLAGS_INIT="-O3 -march=native -mtune=native -pipe"
|
||||
cmake --build build/darling-src/build --parallel "${JOBS:-$(nproc)}"
|
||||
echo 'Build complete. Install the base runtime with: sudo cmake --install build/darling-src/build'
|
||||
@@ -14,4 +14,9 @@ chmod 1777 "$HOME/.darling/private/tmp"
|
||||
python3 build/runtime/tools/stop-darling-runtime.py
|
||||
sh build/runtime/tools/install-riot-presentation-runtime.sh
|
||||
sh build/runtime/tools/install-riot-gpu-runtime.sh
|
||||
# darling-lol perf: build and install optimized libc/libc++/libplatform.
|
||||
if [ -f build/runtime/tools/build-riot-libs-runtime.sh ]; then
|
||||
sh build/runtime/tools/build-riot-libs-runtime.sh
|
||||
python3 build/runtime/tools/install-riot-libs-runtime.py
|
||||
fi
|
||||
python3 build/runtime/tools/install-darling-system-profiler.py
|
||||
@@ -4,4 +4,9 @@ repo=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
cd "$repo"
|
||||
python3 scripts/configure.py >/dev/null
|
||||
if [ -d build/venv/bin ]; then PATH="$repo/build/venv/bin:$PATH"; export PATH; fi
|
||||
# darling-lol perf: optionally wrap with gamemoderun for automatic
|
||||
# CPU governor, nice level, and scheduler optimizations.
|
||||
if [ "${RCLOL_GAMEMODE:-auto}" != "0" ] && command -v gamemoderun >/dev/null 2>&1; then
|
||||
exec gamemoderun sh build/runtime/tools/run-riot-client.sh
|
||||
fi
|
||||
exec sh build/runtime/tools/run-riot-client.sh
|
||||
+4
-1
@@ -4,7 +4,10 @@ import ast,json,re,subprocess,sys
|
||||
from pathlib import Path
|
||||
from common import ROOT
|
||||
subprocess.run([sys.executable,str(ROOT/'scripts/configure.py')],check=True)
|
||||
files=subprocess.check_output(['git','-C',str(ROOT),'ls-files','-z']).decode().split('\0')
|
||||
try:
|
||||
files=subprocess.check_output(['git','-C',str(ROOT),'ls-files','-z'],stderr=subprocess.DEVNULL).decode().split('\0')
|
||||
except Exception:
|
||||
files=[str(p.relative_to(ROOT)) for p in ROOT.rglob('*') if p.is_file() and not any(part.startswith('.') or part in ['build','__pycache__'] for part in p.relative_to(ROOT).parts)]
|
||||
errors=[]
|
||||
secret_patterns=[r'gh[pousr]_[A-Za-z0-9]{30,}',r'AKIA[0-9A-Z]{16}',r'eyJ[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]{20,}',r'-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----']
|
||||
for name in filter(None,files):
|
||||
|
||||
@@ -15,7 +15,7 @@ link = shlex.split((framework / 'CMakeFiles/AppKit.dir/link.txt').read_text())
|
||||
for name in ['NSResponder', 'NSApplication', 'NSWindow', 'NSDisplay', 'NSView', 'NSThemeFrame', 'NSTextInput', 'NSBitmapImageRep', 'NSEvent.subproj/NSEvent_mouse']:
|
||||
obj = project / f'build/riot-{Path(name).name}.o'
|
||||
subprocess.run(['clang', *flags['C_DEFINES'], *flags['C_INCLUDES'], *flags['C_FLAGS'],
|
||||
'-O2', '-Wno-macro-redefined', '-c',
|
||||
'-O3', '-march=native', '-mtune=native', '-fvisibility=hidden', '-Wno-macro-redefined', '-c',
|
||||
str(project / f'build/darling-src/src/external/cocotron/AppKit/{name}.m'),
|
||||
'-o', str(obj)], check=True)
|
||||
link[link.index(f'CMakeFiles/AppKit.dir/{name}.m.o')] = str(obj)
|
||||
@@ -24,7 +24,7 @@ subprocess.run(link, cwd=framework, check=True)
|
||||
for name in ['riot-event-coalescing-check', 'riot-scroll-event-check', 'riot-appkit-pump-check', 'riot-window-layout-check']:
|
||||
probe = project / f'build/{name}.o'
|
||||
subprocess.run(['clang', *flags['C_DEFINES'], *flags['C_INCLUDES'], *flags['C_FLAGS'],
|
||||
'-O2', '-fblocks', '-Wno-macro-redefined', '-c',
|
||||
'-O3', '-march=native', '-mtune=native', '-fblocks', '-Wno-macro-redefined', '-c',
|
||||
str(project / f'tools/{name}.m'), '-o', str(probe)], check=True)
|
||||
subprocess.run(['clang', '-target', 'x86_64-apple-macosx11.0', '-fuse-ld=lld', '-nostdlib',
|
||||
'-isysroot', '@DARLING_SDK@', str(probe), '-lSystem',
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Rebuild libc++abi and libc++ with -O2. The CMake tree has no build type, so the stock objects (and the earlier
|
||||
"""Rebuild libc++abi and libc++ with -O3. The CMake tree has no build type, so the stock objects (and the earlier
|
||||
single-file libc++abi rebuild) are -O0: std::type_info::operator==, __dynamic_cast, shared_ptr internals ran 3-5x slower
|
||||
than they should (about 2% of the League main thread). Outputs build/riot-opt-libc++abi.dylib / riot-opt-libc++.1.dylib.
|
||||
"""
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from pathlib import Path
|
||||
import re, shlex, subprocess, sys
|
||||
import os, re, shlex, subprocess, sys
|
||||
root = Path(__file__).resolve().parent.parent
|
||||
resource_dir = subprocess.check_output(['clang', '-print-resource-dir'], text=True).strip()
|
||||
def current_clang(values):
|
||||
@@ -26,9 +26,9 @@ def build(lib, objdir, linkdir, output):
|
||||
jobs.append((rel, obj)); link[i] = str(obj)
|
||||
def compile_one(job):
|
||||
rel, obj = job
|
||||
subprocess.run(['clang++', *flags['CXX_DEFINES'], *flags['CXX_INCLUDES'], *flags['CXX_FLAGS'], '-O2', '-DNDEBUG',
|
||||
subprocess.run(['clang++', *flags['CXX_DEFINES'], *flags['CXX_INCLUDES'], *flags['CXX_FLAGS'], '-O3', '-march=native', '-mtune=native', '-fomit-frame-pointer', '-DNDEBUG',
|
||||
'-c', str(src / rel), '-o', str(obj)], check=True)
|
||||
with ThreadPoolExecutor(max_workers=8) as pool:
|
||||
with ThreadPoolExecutor(max_workers=os.cpu_count() or 8) as pool:
|
||||
list(pool.map(compile_one, jobs))
|
||||
link[link.index('-o') + 1] = str(root / 'build' / output)
|
||||
subprocess.run(link, cwd=folder, check=True)
|
||||
|
||||
@@ -13,7 +13,7 @@ for line in (framework / 'CMakeFiles/X11_backend.dir/flags.make').read_text().sp
|
||||
link = shlex.split((framework / 'CMakeFiles/X11_backend.dir/link.txt').read_text())
|
||||
for name in ('X11Display', 'X11Window'):
|
||||
obj = project / f'build/riot-{name}.o'
|
||||
subprocess.run(['clang', *flags['C_DEFINES'], *flags['C_INCLUDES'], *flags['C_FLAGS'], '-O2', '-Wno-macro-redefined', '-c', str(project / f'build/darling-src/src/external/cocotron/AppKit/X11.backend/{name}.m'), '-o', str(obj)], check=True)
|
||||
subprocess.run(['clang', *flags['C_DEFINES'], *flags['C_INCLUDES'], *flags['C_FLAGS'], '-O3', '-march=native', '-mtune=native', '-fvisibility=hidden', '-Wno-macro-redefined', '-c', str(project / f'build/darling-src/src/external/cocotron/AppKit/X11.backend/{name}.m'), '-o', str(obj)], check=True)
|
||||
link[link.index(f'CMakeFiles/X11_backend.dir/X11.backend/{name}.m.o')] = str(obj)
|
||||
link[link.index('-o') + 1] = str(project / 'build/riot-X11')
|
||||
subprocess.run(link, cwd=framework, check=True)
|
||||
@@ -22,7 +22,7 @@ print('Built isolated X11 backend with native visibility events')
|
||||
# Exercise actual backend modifier transitions without compositor shortcuts.
|
||||
probe = project / 'build/riot-modifier-check.o'
|
||||
subprocess.run(['clang', *flags['C_DEFINES'], *flags['C_INCLUDES'], *flags['C_FLAGS'],
|
||||
'-O2', '-Wno-macro-redefined', '-c', str(project / 'tools/riot-modifier-check.m'),
|
||||
'-O3', '-march=native', '-mtune=native', '-Wno-macro-redefined', '-c', str(project / 'tools/riot-modifier-check.m'),
|
||||
'-o', str(probe)], check=True)
|
||||
subprocess.run(['clang', '-target', 'x86_64-apple-macosx11.0', '-fuse-ld=lld',
|
||||
'-nostdlib', '-isysroot', '@DARLING_SDK@', str(probe),
|
||||
|
||||
@@ -12,11 +12,11 @@ make -C "$build_dir/src/external/cfnetwork/src" CFNetwork -j8
|
||||
make -C "$build_dir/src/external/configd/SystemConfiguration.fproj" SystemConfiguration -j8
|
||||
make -C "$build_dir/src/external/cocotron/QuartzCore" QuartzCore -j8
|
||||
make -C "$build_dir/src/external/cocotron/AppKit" AppKit X11_backend -j8
|
||||
cc -shared -fPIC -O2 -Wall -Wextra \
|
||||
cc -shared -fPIC -O3 -march=native -mtune=native -Wall -Wextra \
|
||||
"-Wl,-soname,$project_dir/build/libdarling-xshm.so" \
|
||||
"$project_dir/tools/m58f299-xshm.c" -o "$project_dir/build/libdarling-xshm.next.so" \
|
||||
-pthread $(pkg-config --cflags --libs x11 x11-xcb xcb-shm xext)
|
||||
clang -target x86_64-apple-macosx11.0 -fuse-ld=lld -dynamiclib -nostdlib \
|
||||
clang -target x86_64-apple-macosx11.0 -fuse-ld=lld -dynamiclib -nostdlib -O3 -march=native -mtune=native \
|
||||
-isysroot @DARLING_SDK@ \
|
||||
-I"$source_dir/src/startup/mldr/elfcalls" \
|
||||
"-DDARLING_XSHM_HOST_LIBRARY=\"$project_dir/build/libdarling-xshm.so\"" \
|
||||
|
||||
@@ -135,8 +135,10 @@ 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);
|
||||
/* Wait for server consumption before reusing the segment on the next tick. */
|
||||
XSync(f->display, 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);
|
||||
pthread_mutex_unlock(&f->render);
|
||||
return result;
|
||||
}
|
||||
@@ -150,7 +152,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);
|
||||
XSync(f->display, False);
|
||||
XFlush(f->display);
|
||||
pthread_mutex_unlock(&f->render);
|
||||
return result;
|
||||
}
|
||||
@@ -256,7 +258,9 @@ static void *pumpFrames(void *arg) {
|
||||
XFlush(f->display);
|
||||
}
|
||||
}
|
||||
deadline.tv_nsec += 2000000;
|
||||
/* darling-lol perf: 500µs poll interval (2000 Hz) reduces average frame
|
||||
* detection latency from 1ms to 0.25ms at negligible CPU cost. */
|
||||
deadline.tv_nsec += 500000;
|
||||
if (deadline.tv_nsec >= 1000000000) {
|
||||
deadline.tv_nsec -= 1000000000;
|
||||
deadline.tv_sec++;
|
||||
|
||||
@@ -17,10 +17,14 @@ unset RCLOL_APP_ARG3 RCLOL_APP_ARG3_SLOT RCLOL_UX_ARG1 RCLOL_UX_ARG1_SLOT
|
||||
unset RCLOL_UX_ARG2 RCLOL_UX_ARG2_SLOT
|
||||
# The paired runtime presents completed GPU textures without CPU readback.
|
||||
# Explicit 0 or an empty value selects the CPU fallback for diagnostics.
|
||||
for option in RCLOL_GPU_DIRECT_PRESENT RCLOL_GPU_DIRECT_ASYNC RCLOL_GPU_DIRECT_VSYNC DARLING_CA_PRESENT_AT_COMMIT DARLING_CA_SKIP_LOCAL_IOSURFACE_RENDER RCLOL_FORWARD_MOUSE_MOVED; do
|
||||
for option in RCLOL_GPU_DIRECT_PRESENT RCLOL_GPU_DIRECT_ASYNC DARLING_CA_PRESENT_AT_COMMIT DARLING_CA_SKIP_LOCAL_IOSURFACE_RENDER RCLOL_FORWARD_MOUSE_MOVED; do
|
||||
eval 'value=${'"$option"'-1}'
|
||||
case "$value" in 0|'') unset "$option";; *) export "$option=$value";; esac
|
||||
done
|
||||
# darling-lol perf: VSync off by default for lowest input latency.
|
||||
# Set RCLOL_GPU_DIRECT_VSYNC=1 to re-enable if tearing is unacceptable.
|
||||
eval 'value=${RCLOL_GPU_DIRECT_VSYNC-0}'
|
||||
case "$value" in 0|'') unset RCLOL_GPU_DIRECT_VSYNC;; *) export RCLOL_GPU_DIRECT_VSYNC="$value";; esac
|
||||
prefix=${DPREFIX:-$HOME/.darling}
|
||||
# The first-time installer puts the client in /Users/Shared/Riot Games and then
|
||||
# hands off through LaunchServices, which does not start it under Darling. Link
|
||||
@@ -60,9 +64,24 @@ else
|
||||
--launch-patchline="${RCLOL_LAUNCH_PATCHLINE-live}"
|
||||
fi
|
||||
fi
|
||||
# --- darling-lol perf: low-latency GPU driver overrides ---
|
||||
# Vulkan WSI: prefer immediate (no VSync) or mailbox (tear-free low-latency).
|
||||
export MESA_VK_WSI_PRESENT_MODE="${MESA_VK_WSI_PRESENT_MODE:-immediate}"
|
||||
# Mesa OpenGL: disable vertical sync, skip error checking.
|
||||
export vblank_mode="${vblank_mode:-0}"
|
||||
export MESA_NO_ERROR="${MESA_NO_ERROR:-1}"
|
||||
# NVIDIA proprietary driver: disable VSync, limit pre-rendered frames.
|
||||
export __GL_SYNC_TO_VBLANK="${__GL_SYNC_TO_VBLANK:-0}"
|
||||
export __GL_MaxFramesAllowed="${__GL_MaxFramesAllowed:-1}"
|
||||
export __GL_THREADED_OPTIMIZATIONS="${__GL_THREADED_OPTIMIZATIONS:-1}"
|
||||
export __GL_YIELD="${__GL_YIELD:-NOTHING}"
|
||||
# Reduce timer slack for tighter scheduling in the game process tree.
|
||||
if [ -w /proc/self/timerslack_ns ]; then
|
||||
echo 1 > /proc/self/timerslack_ns 2>/dev/null || true
|
||||
fi
|
||||
exec env -u ELECTRON_RUN_AS_NODE -u DYLD_INSERT_LIBRARIES EGL_PLATFORM=x11 \
|
||||
LANG="${RCLOL_GUEST_LOCALE:-en_US.UTF-8}" LC_ALL="${RCLOL_GUEST_LOCALE:-en_US.UTF-8}" \
|
||||
mesa_glthread="${RCLOL_MESA_GLTHREAD-${mesa_glthread-false}}" \
|
||||
mesa_glthread="${RCLOL_MESA_GLTHREAD-${mesa_glthread-true}}" \
|
||||
XMODIFIERS="${RCLOL_XMODIFIERS:-@im=none}" DISPLAY="${DISPLAY:-:1}" \
|
||||
DYLD_LIBRARY_PATH="${DYLD_LIBRARY_PATH:-@GUEST_HOME@/RuntimeLibraries}" \
|
||||
DYLD_FRAMEWORK_PATH="${DYLD_FRAMEWORK_PATH:-@GUEST_HOME@/RuntimeFrameworks}" \
|
||||
|
||||
Reference in new issue
Block a user