Reapply "Prevent iOS daemon throttling with SSH launch and priority boosts"
This reverts commit 4ba8384929.
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
#import "FrameUpdater.h"
|
#import "FrameUpdater.h"
|
||||||
|
#import <mach/mach_time.h>
|
||||||
|
|
||||||
@implementation FrameUpdater {
|
@implementation FrameUpdater {
|
||||||
|
|
||||||
@@ -6,6 +7,7 @@
|
|||||||
NSOperationQueue *_q;
|
NSOperationQueue *_q;
|
||||||
BOOL _updatingFrames;
|
BOOL _updatingFrames;
|
||||||
CADisplayLink *_displayLink;
|
CADisplayLink *_displayLink;
|
||||||
|
dispatch_source_t _timer;
|
||||||
|
|
||||||
// Shared from ScreenDumpVNC
|
// Shared from ScreenDumpVNC
|
||||||
IOSurfaceRef _screenSurface;
|
IOSurfaceRef _screenSurface;
|
||||||
@@ -20,6 +22,14 @@
|
|||||||
// Dual capture mode support
|
// Dual capture mode support
|
||||||
IOSurfaceRef _renderServerSurface; // Separate surface for CARenderServer (avoids framebuffer locking)
|
IOSurfaceRef _renderServerSurface; // Separate surface for CARenderServer (avoids framebuffer locking)
|
||||||
CaptureMode(^_captureModeBlock)(void);
|
CaptureMode(^_captureModeBlock)(void);
|
||||||
|
|
||||||
|
// Diagnostics
|
||||||
|
uint64_t _timerFires;
|
||||||
|
uint64_t _framesRendered;
|
||||||
|
uint64_t _framesSkipped;
|
||||||
|
uint64_t _lastStatsTime;
|
||||||
|
uint64_t _totalCaptureTime;
|
||||||
|
uint64_t _maxCaptureTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
-(instancetype)initWithSurfaceInfo:(IOSurfaceRef)screenSurface
|
-(instancetype)initWithSurfaceInfo:(IOSurfaceRef)screenSurface
|
||||||
@@ -35,6 +45,7 @@
|
|||||||
if ((self = [super init])) {
|
if ((self = [super init])) {
|
||||||
_q = [[NSOperationQueue alloc] init];
|
_q = [[NSOperationQueue alloc] init];
|
||||||
_q.maxConcurrentOperationCount = 1; // Serialize to prevent frame queue buildup
|
_q.maxConcurrentOperationCount = 1; // Serialize to prevent frame queue buildup
|
||||||
|
_q.qualityOfService = NSQualityOfServiceUserInteractive;
|
||||||
_updatingFrames = NO;
|
_updatingFrames = NO;
|
||||||
_displayLink = nil;
|
_displayLink = nil;
|
||||||
|
|
||||||
@@ -60,12 +71,19 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_timerFires++;
|
||||||
|
|
||||||
// Skip if queue is busy to prevent frame buildup and reduce lock contention
|
// Skip if queue is busy to prevent frame buildup and reduce lock contention
|
||||||
if (_q.operationCount > 0) {
|
if (_q.operationCount > 0) {
|
||||||
|
_framesSkipped++;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
[_q addOperationWithBlock: ^{
|
[_q addOperationWithBlock: ^{
|
||||||
|
_framesRendered++;
|
||||||
|
|
||||||
|
uint64_t captureStart = mach_absolute_time();
|
||||||
|
|
||||||
// Get current capture mode
|
// Get current capture mode
|
||||||
CaptureMode mode = _captureModeBlock ? _captureModeBlock() : CaptureModeCARenderServer;
|
CaptureMode mode = _captureModeBlock ? _captureModeBlock() : CaptureModeCARenderServer;
|
||||||
BOOL needsScaling = (_width != _nativeWidth || _height != _nativeHeight);
|
BOOL needsScaling = (_width != _nativeWidth || _height != _nativeHeight);
|
||||||
@@ -86,28 +104,78 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint64_t captureEnd = mach_absolute_time();
|
||||||
|
uint64_t captureDuration = captureEnd - captureStart;
|
||||||
|
_totalCaptureTime += captureDuration;
|
||||||
|
if (captureDuration > _maxCaptureTime) _maxCaptureTime = captureDuration;
|
||||||
|
|
||||||
rfbMarkRectAsModified(_rfbScreenInfo, 0, 0, _width, _height);
|
rfbMarkRectAsModified(_rfbScreenInfo, 0, 0, _width, _height);
|
||||||
|
|
||||||
|
// Log stats every second
|
||||||
|
uint64_t now = mach_absolute_time();
|
||||||
|
if (_lastStatsTime == 0) _lastStatsTime = now;
|
||||||
|
|
||||||
|
mach_timebase_info_data_t timebase;
|
||||||
|
mach_timebase_info(&timebase);
|
||||||
|
uint64_t elapsed_ns = (now - _lastStatsTime) * timebase.numer / timebase.denom;
|
||||||
|
|
||||||
|
if (elapsed_ns >= NSEC_PER_SEC) {
|
||||||
|
double elapsed_sec = (double)elapsed_ns / NSEC_PER_SEC;
|
||||||
|
double avgCaptureMs = (_framesRendered > 0) ?
|
||||||
|
((double)_totalCaptureTime * timebase.numer / timebase.denom / _framesRendered / 1000000.0) : 0;
|
||||||
|
double maxCaptureMs = (double)_maxCaptureTime * timebase.numer / timebase.denom / 1000000.0;
|
||||||
|
|
||||||
|
NSLog(@"[FPS] fps=%.1f skip=%.0f%% | capture: avg=%.1fms max=%.1fms",
|
||||||
|
_framesRendered / elapsed_sec,
|
||||||
|
(_timerFires > 0) ? (100.0 * _framesSkipped / _timerFires) : 0.0,
|
||||||
|
avgCaptureMs, maxCaptureMs);
|
||||||
|
_timerFires = 0;
|
||||||
|
_framesRendered = 0;
|
||||||
|
_framesSkipped = 0;
|
||||||
|
_totalCaptureTime = 0;
|
||||||
|
_maxCaptureTime = 0;
|
||||||
|
_lastStatsTime = now;
|
||||||
|
}
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
-(void)stopFrameLoop {
|
-(void)stopFrameLoop {
|
||||||
if (_displayLink == nil) return;
|
_updatingFrames = NO;
|
||||||
|
|
||||||
|
if (_timer) {
|
||||||
|
dispatch_source_cancel(_timer);
|
||||||
|
_timer = nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_displayLink) {
|
||||||
dispatch_async(dispatch_get_main_queue(), ^(void){
|
dispatch_async(dispatch_get_main_queue(), ^(void){
|
||||||
[_displayLink invalidate];
|
[_displayLink invalidate];
|
||||||
_displayLink = nil;
|
_displayLink = nil;
|
||||||
_updatingFrames = NO;
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
-(void)startFrameLoop {
|
-(void)startFrameLoop {
|
||||||
[self stopFrameLoop];
|
[self stopFrameLoop];
|
||||||
_updatingFrames = YES;
|
_updatingFrames = YES;
|
||||||
|
|
||||||
CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(_updateFrame)];
|
// Use high-priority dispatch timer instead of CADisplayLink
|
||||||
displayLink.preferredFramesPerSecond = 60; // Limit to 60 FPS
|
// CADisplayLink may be throttled for daemon processes
|
||||||
[displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
|
dispatch_queue_attr_t attr = dispatch_queue_attr_make_with_qos_class(
|
||||||
_displayLink = displayLink;
|
DISPATCH_QUEUE_SERIAL, QOS_CLASS_USER_INTERACTIVE, 0);
|
||||||
|
dispatch_queue_t timerQueue = dispatch_queue_create("com.mousen.screendump.frametimer", attr);
|
||||||
|
|
||||||
|
_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, DISPATCH_TIMER_STRICT, timerQueue);
|
||||||
|
|
||||||
|
// 60 FPS = ~16.67ms interval
|
||||||
|
uint64_t interval = NSEC_PER_SEC / 60;
|
||||||
|
dispatch_source_set_timer(_timer, dispatch_time(DISPATCH_TIME_NOW, 0), interval, 0);
|
||||||
|
|
||||||
|
dispatch_source_set_event_handler(_timer, ^{
|
||||||
|
[self _updateFrame];
|
||||||
|
});
|
||||||
|
|
||||||
|
dispatch_resume(_timer);
|
||||||
}
|
}
|
||||||
|
|
||||||
-(void)dealloc {
|
-(void)dealloc {
|
||||||
|
|||||||
@@ -2,19 +2,20 @@ export THEOS_PACKAGE_SCHEME = rootless
|
|||||||
export ARCHS = arm64
|
export ARCHS = arm64
|
||||||
export TARGET = iphone:16.5:14.0
|
export TARGET = iphone:16.5:14.0
|
||||||
export GO_EASY_ON_ME = 1
|
export GO_EASY_ON_ME = 1
|
||||||
export COPYFILE_DISABLE=1
|
export COPYFILE_DISABLE = 1
|
||||||
|
|
||||||
include $(THEOS)/makefiles/common.mk
|
include $(THEOS)/makefiles/common.mk
|
||||||
|
|
||||||
TOOL_NAME = screendumpd
|
TOOL_NAME = screendumpd
|
||||||
$(TOOL_NAME)_FILES = $(wildcard *.m)
|
|
||||||
$(TOOL_NAME)_FRAMEWORKS := IOSurface IOKit
|
screendumpd_FILES = $(wildcard *.m)
|
||||||
$(TOOL_NAME)_PRIVATE_FRAMEWORKS := IOMobileFramebuffer IOSurface
|
screendumpd_FRAMEWORKS = IOSurface IOKit
|
||||||
$(TOOL_NAME)_OBJCFLAGS += -I./vncbuild/include -Iinclude -fobjc-arc
|
screendumpd_PRIVATE_FRAMEWORKS = IOMobileFramebuffer IOSurface
|
||||||
$(TOOL_NAME)_LDFLAGS += -Wl,-segalign,4000 -L./vncbuild/lib -lvncserver -lpng -llzo2 -ljpeg -lssl -lcrypto -lz
|
screendumpd_OBJCFLAGS = -I./vncbuild/include -Iinclude -fobjc-arc
|
||||||
$(TOOL_NAME)_CFLAGS = -w
|
screendumpd_LDFLAGS = -Wl,-segalign,4000 -L./vncbuild/lib -lvncserver -lpng -llzo2 -ljpeg -lssl -lcrypto -lz
|
||||||
$(TOOL_NAME)_CODESIGN_FLAGS = "-Sen.plist"
|
screendumpd_CFLAGS = -w
|
||||||
$(TOOL_NAME)_INSTALL_PATH = /usr/libexec
|
screendumpd_CODESIGN_FLAGS = -Sen.plist
|
||||||
|
screendumpd_INSTALL_PATH = /usr/libexec
|
||||||
|
|
||||||
before-stage::
|
before-stage::
|
||||||
$(ECHO_NOTHING)find . -name '.DS_Store' -type f -delete$(ECHO_END)
|
$(ECHO_NOTHING)find . -name '.DS_Store' -type f -delete$(ECHO_END)
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
#import <rfb/rfb.h>
|
#import <rfb/rfb.h>
|
||||||
#import "IOMobileFramebuffer.h"
|
#import "IOMobileFramebuffer.h"
|
||||||
|
#import <IOKit/pwr_mgt/IOPMLib.h>
|
||||||
|
|
||||||
@implementation ScreenDumpVNC {
|
@implementation ScreenDumpVNC {
|
||||||
int _prefsHeight;
|
int _prefsHeight;
|
||||||
@@ -38,6 +39,25 @@
|
|||||||
+(void)load {
|
+(void)load {
|
||||||
ScreenDumpVNC* sharedInstance = [self sharedInstance];
|
ScreenDumpVNC* sharedInstance = [self sharedInstance];
|
||||||
if (![sharedInstance enabled]) return;
|
if (![sharedInstance enabled]) return;
|
||||||
|
|
||||||
|
// Tell iOS this is latency-critical work - prevents throttling for daemons
|
||||||
|
[[NSProcessInfo processInfo] beginActivityWithOptions:(NSActivityLatencyCritical | NSActivityUserInitiated)
|
||||||
|
reason:@"VNC screen streaming"];
|
||||||
|
|
||||||
|
// Power assertion to prevent display/GPU throttling
|
||||||
|
IOPMAssertionID assertionID;
|
||||||
|
IOPMAssertionCreateWithName(kIOPMAssertionTypePreventUserIdleDisplaySleep,
|
||||||
|
kIOPMAssertionLevelOn,
|
||||||
|
CFSTR("VNC screen capture requires display access"),
|
||||||
|
&assertionID);
|
||||||
|
|
||||||
|
// Also assert we need system activity (prevents CPU throttling)
|
||||||
|
IOPMAssertionID systemAssertionID;
|
||||||
|
IOPMAssertionCreateWithName(kIOPMAssertionTypePreventUserIdleSystemSleep,
|
||||||
|
kIOPMAssertionLevelOn,
|
||||||
|
CFSTR("VNC server active"),
|
||||||
|
&systemAssertionID);
|
||||||
|
|
||||||
[sharedInstance setupScreenInfo];
|
[sharedInstance setupScreenInfo];
|
||||||
[sharedInstance startVNCServer];
|
[sharedInstance startVNCServer];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,40 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Setup passwordless SSH for root to localhost (required for screendumpd performance)
|
||||||
|
SSH_DIR="/var/jb/var/root/.ssh"
|
||||||
|
KEY_FILE="$SSH_DIR/id_ed25519"
|
||||||
|
AUTH_KEYS="$SSH_DIR/authorized_keys"
|
||||||
|
|
||||||
|
# Create .ssh directory if it doesn't exist
|
||||||
|
if [ ! -d "$SSH_DIR" ]; then
|
||||||
|
mkdir -p "$SSH_DIR"
|
||||||
|
chmod 700 "$SSH_DIR"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Generate ed25519 key if it doesn't exist
|
||||||
|
if [ ! -f "$KEY_FILE" ]; then
|
||||||
|
ssh-keygen -t ed25519 -N "" -f "$KEY_FILE" -q
|
||||||
|
chmod 600 "$KEY_FILE"
|
||||||
|
chmod 644 "${KEY_FILE}.pub"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Add public key to authorized_keys if not already present
|
||||||
|
if [ -f "${KEY_FILE}.pub" ]; then
|
||||||
|
PUB_KEY=$(cat "${KEY_FILE}.pub")
|
||||||
|
|
||||||
|
# Create authorized_keys if it doesn't exist
|
||||||
|
if [ ! -f "$AUTH_KEYS" ]; then
|
||||||
|
touch "$AUTH_KEYS"
|
||||||
|
chmod 600 "$AUTH_KEYS"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if key is already in authorized_keys
|
||||||
|
if ! grep -qF "$PUB_KEY" "$AUTH_KEYS" 2>/dev/null; then
|
||||||
|
echo "$PUB_KEY" >> "$AUTH_KEYS"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Load the launch daemon
|
||||||
launchctl load /var/jb/Library/LaunchDaemons/com.mousen.screendumpd.plist 2> /dev/null
|
launchctl load /var/jb/Library/LaunchDaemons/com.mousen.screendumpd.plist 2> /dev/null
|
||||||
exit 0;
|
|
||||||
|
exit 0
|
||||||
@@ -6,6 +6,14 @@
|
|||||||
<string>com.mousen.screendumpd</string>
|
<string>com.mousen.screendumpd</string>
|
||||||
<key>ProgramArguments</key>
|
<key>ProgramArguments</key>
|
||||||
<array>
|
<array>
|
||||||
|
<string>/var/jb/usr/bin/ssh</string>
|
||||||
|
<string>-o</string>
|
||||||
|
<string>StrictHostKeyChecking=no</string>
|
||||||
|
<string>-o</string>
|
||||||
|
<string>UserKnownHostsFile=/dev/null</string>
|
||||||
|
<string>-o</string>
|
||||||
|
<string>BatchMode=yes</string>
|
||||||
|
<string>root@localhost</string>
|
||||||
<string>/var/jb/usr/libexec/screendumpd</string>
|
<string>/var/jb/usr/libexec/screendumpd</string>
|
||||||
</array>
|
</array>
|
||||||
<key>RunAtLoad</key>
|
<key>RunAtLoad</key>
|
||||||
@@ -22,5 +30,13 @@
|
|||||||
<key>Core</key>
|
<key>Core</key>
|
||||||
<integer>9223372036854775807</integer>
|
<integer>9223372036854775807</integer>
|
||||||
</dict>
|
</dict>
|
||||||
|
<key>ProcessType</key>
|
||||||
|
<string>Interactive</string>
|
||||||
|
<key>Nice</key>
|
||||||
|
<integer>-10</integer>
|
||||||
|
<key>LegacyTimers</key>
|
||||||
|
<true/>
|
||||||
|
<key>UserName</key>
|
||||||
|
<string>root</string>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -1,12 +1,44 @@
|
|||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
#import <stdio.h>
|
#import <stdio.h>
|
||||||
|
#import <signal.h>
|
||||||
|
#import <unistd.h>
|
||||||
|
#import <pthread.h>
|
||||||
|
#import <sched.h>
|
||||||
|
#import <dispatch/dispatch.h>
|
||||||
#import "ScreenDumpVNC.h"
|
#import "ScreenDumpVNC.h"
|
||||||
#import "utils.h"
|
#import "utils.h"
|
||||||
|
|
||||||
#define kPreferencesNotify "com.mousen.screendump/restart"
|
#define kPreferencesNotify "com.mousen.screendump/restart"
|
||||||
|
|
||||||
|
static void signalHandler(int sig) {
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[], char *envp[]) {
|
int main(int argc, char *argv[], char *envp[]) {
|
||||||
@autoreleasepool {
|
@autoreleasepool {
|
||||||
|
// Handle termination signals (sent by launchd or when SSH disconnects)
|
||||||
|
signal(SIGTERM, signalHandler);
|
||||||
|
signal(SIGHUP, signalHandler);
|
||||||
|
signal(SIGINT, signalHandler);
|
||||||
|
|
||||||
|
// Monitor parent process - exit if parent (sshd) dies (ppid becomes 1)
|
||||||
|
pid_t originalPpid = getppid();
|
||||||
|
dispatch_source_t parentTimer = dispatch_source_create(
|
||||||
|
DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
|
||||||
|
dispatch_source_set_timer(parentTimer, DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC, 0);
|
||||||
|
dispatch_source_set_event_handler(parentTimer, ^{
|
||||||
|
if (getppid() != originalPpid) {
|
||||||
|
// Parent changed (likely died and we got reparented to launchd/init)
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
dispatch_resume(parentTimer);
|
||||||
|
|
||||||
|
// Boost main thread to real-time priority for better daemon performance
|
||||||
|
struct sched_param param;
|
||||||
|
param.sched_priority = sched_get_priority_max(SCHED_RR);
|
||||||
|
pthread_setschedparam(pthread_self(), SCHED_RR, ¶m);
|
||||||
|
|
||||||
CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), NULL, (CFNotificationCallback)exitProcess, CFSTR(kPreferencesNotify), NULL, CFNotificationSuspensionBehaviorDeliverImmediately);
|
CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), NULL, (CFNotificationCallback)exitProcess, CFSTR(kPreferencesNotify), NULL, CFNotificationSuspensionBehaviorDeliverImmediately);
|
||||||
|
|
||||||
[ScreenDumpVNC load];
|
[ScreenDumpVNC load];
|
||||||
|
|||||||
Reference in New Issue
Block a user