preferences: support changing renderer backend

This commit is contained in:
osy
2022-12-27 15:33:07 -08:00
parent e12115c1d9
commit 201a2206f3
12 changed files with 108 additions and 11 deletions
+1
View File
@@ -28,6 +28,7 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, readonly) BOOL hasRemoteProcess;
@property (nonatomic, readonly) NSURL *libraryURL;
@property (nonatomic) NSArray<NSString *> *argv;
@property (nonatomic, nullable) NSDictionary<NSString *, NSString *> *environment;
@property (nonatomic) dispatch_semaphore_t done;
@property (nonatomic) NSInteger status;
@property (nonatomic) NSInteger fatal;
+1
View File
@@ -181,6 +181,7 @@
}
NSFileHandle *standardOutput = self.logging.standardOutput.fileHandleForWriting;
NSFileHandle *standardError = self.logging.standardError.fileHandleForWriting;
[_connection.remoteObjectProxy setEnvironment:self.environment];
[[_connection remoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {
if (error.domain == NSCocoaErrorDomain && error.code == NSXPCConnectionInvalid) {
completion(YES, nil); // inhibit this error since we always see it on quit
+9
View File
@@ -16,11 +16,20 @@
#import "UTMQemu.h"
/// Specify the backend renderer for this VM
typedef NS_ENUM(NSInteger, UTMQEMURendererBackend) {
kQEMURendererBackendDefault = 0,
kQEMURendererBackendAngleGL = 1,
kQEMURendererBackendAngleMetal = 2,
kQEMURendererBackendMax = 3,
};
NS_ASSUME_NONNULL_BEGIN
@interface UTMQemuSystem : UTMQemu
@property (nonatomic, nullable, copy) NSArray<NSURL *> *resources;
@property (nonatomic) UTMQEMURendererBackend rendererBackend;
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithArguments:(NSArray<NSString *> *)arguments NS_UNAVAILABLE;
+34 -1
View File
@@ -32,19 +32,32 @@
static void *start_qemu(void *args) {
UTMQemuSystem *self = (__bridge_transfer UTMQemuSystem *)args;
NSArray<NSString *> *qemuArgv = self.argv;
NSMutableArray<NSString *> *environment = [NSMutableArray arrayWithCapacity:self.environment.count];
NSCAssert(self->_qemu_init != NULL, @"Started thread with invalid function.");
NSCAssert(self->_qemu_main_loop != NULL, @"Started thread with invalid function.");
NSCAssert(self->_qemu_cleanup != NULL, @"Started thread with invalid function.");
NSCAssert(qemuArgv, @"Started thread with invalid argv.");
/* set up environment variables */
[self.environment enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *value, BOOL *stop) {
NSString *combined = [NSString stringWithFormat:@"%@=%@", key, value];
[environment addObject:combined];
setenv(key.UTF8String, value.UTF8String, 1);
}];
NSUInteger envc = environment.count;
const char *envp[envc + 1];
for (NSUInteger i = 0; i < envc; i++) {
envp[i] = environment[i].UTF8String;
}
envp[envc] = NULL;
int argc = (int)qemuArgv.count + 1;
const char *argv[argc];
argv[0] = "qemu-system";
for (int i = 0; i < qemuArgv.count; i++) {
argv[i+1] = [qemuArgv[i] UTF8String];
}
const char *envp[] = { NULL };
self->_qemu_init(argc, argv, envp);
self->_qemu_main_loop();
self->_qemu_cleanup();
@@ -53,6 +66,26 @@ static void *start_qemu(void *args) {
return NULL;
}
- (void)setRendererBackend:(UTMQEMURendererBackend)rendererBackend {
_rendererBackend = rendererBackend;
switch (rendererBackend) {
case kQEMURendererBackendAngleGL:
self.environment = @{@"ANGLE_DEFAULT_PLATFORM": @"gl"};
break;
case kQEMURendererBackendAngleMetal:
self.environment = @{@"ANGLE_DEFAULT_PLATFORM": @"metal"};
break;
case kQEMURendererBackendDefault:
#if TARGET_OS_IPHONE
// on iOS, use Metal as the default because it is more stable
self.environment = @{@"ANGLE_DEFAULT_PLATFORM": @"metal"};
#endif
break;
default:
break;
}
}
- (instancetype)initWithArguments:(NSArray<NSString *> *)arguments architecture:(nonnull NSString *)architecture {
self = [super initWithArguments:arguments];
if (self) {
+11
View File
@@ -45,6 +45,7 @@ NSString *const kSuspendSnapshotName = @"suspend";
@property (nonatomic, nullable) dispatch_semaphore_t qemuDidConnectEvent;
@property (nonatomic) BOOL changeCursorRequestInProgress;
@property (nonatomic, nullable) NSString *lastErrorLine;
@property (nonatomic, readonly) UTMQEMURendererBackend rendererBackend;
@end
@@ -68,6 +69,15 @@ NSString *const kSuspendSnapshotName = @"suspend";
_isGuestToolsInstallRequested = isGuestToolsInstallRequested;
}
- (UTMQEMURendererBackend)rendererBackend {
NSInteger value = [NSUserDefaults.standardUserDefaults integerForKey:@"QEMURendererBackend"];
if (value >= 0 && value < kQEMURendererBackendMax) {
return (UTMQEMURendererBackend)value;
} else {
return kQEMURendererBackendDefault;
}
}
- (instancetype)initWithConfiguration:(UTMConfigurationWrapper *)configuration packageURL:(NSURL *)packageURL {
self = [super initWithConfiguration:configuration packageURL:packageURL];
if (self) {
@@ -219,6 +229,7 @@ NSString *const kSuspendSnapshotName = @"suspend";
__weak typeof(self) weakSelf = self;
__block NSError *qemuStartError = nil;
dispatch_semaphore_t spiceConnectOrErrorEvent = dispatch_semaphore_create(0);
self.system.rendererBackend = self.rendererBackend;
[self.system startWithCompletion:^(BOOL success, NSString *msg){
typeof(self) _self = weakSelf;
if (!_self) {
+28
View File
@@ -94,6 +94,34 @@
<key>DefaultValue</key>
<false/>
</dict>
<dict>
<key>Type</key>
<string>PSGroupSpecifier</string>
<key>Title</key>
<string>Graphics</string>
</dict>
<dict>
<key>Type</key>
<string>PSMultiValueSpecifier</string>
<key>Title</key>
<string>Renderer Backend</string>
<key>Key</key>
<string>QEMURendererBackend</string>
<key>DefaultValue</key>
<integer>0</integer>
<key>Titles</key>
<array>
<string>Default</string>
<string>ANGLE (OpenGL)</string>
<string>ANGLE (Metal)</string>
</array>
<key>Values</key>
<array>
<integer>0</integer>
<integer>1</integer>
<integer>2</integer>
</array>
</dict>
<dict>
<key>Type</key>
<string>PSGroupSpecifier</string>
+7
View File
@@ -28,6 +28,7 @@ struct SettingsView: View {
@AppStorage("IsCapsLockKey") var isCapsLockKey = false
@AppStorage("NoSaveScreenshot") var isNoSaveScreenshot = false
@AppStorage("InvertScroll") var isInvertScroll = false
@AppStorage("QEMURendererBackend") var qemuRendererBackend: UTMQEMURendererBackend = .qemuRendererBackendDefault
var body: some View {
Form {
@@ -56,6 +57,11 @@ struct SettingsView: View {
Toggle(isOn: $isNoSaveScreenshot) {
Text("Do not save VM screenshot to disk")
}.help("If enabled, any existing screenshot will be deleted the next time the VM is started.")
Picker("QEMU Renderer Backend", selection: $qemuRendererBackend) {
Text("Default").tag(UTMQEMURendererBackend.qemuRendererBackendDefault)
Text("ANGLE (OpenGL)").tag(UTMQEMURendererBackend.qemuRendererBackendAngleGL)
Text("ANGLE (Metal)").tag(UTMQEMURendererBackend.qemuRendererBackendAngleMetal)
}.help("By default, the best renderer for this device will be used. You can override this with to always use a specific renderer. This only applies to QEMU VMs with GPU accelerated graphics.")
}
Section(header: Text("Input")) {
Toggle(isOn: $isCtrlRightClick, label: {
@@ -93,6 +99,7 @@ extension UserDefaults {
@objc dynamic var IsCapsLockKey: Bool { false }
@objc dynamic var NoSaveScreenshot: Bool { false }
@objc dynamic var InvertScroll: Bool { false }
@objc dynamic var QEMURendererBackend: Int { 0 }
}
@available(macOS 11, *)
+5 -1
View File
@@ -26,6 +26,8 @@
@implementation QEMUHelper
@synthesize environment;
- (instancetype)init {
if (self = [super init]) {
self.urls = [NSMutableArray array];
@@ -112,7 +114,9 @@
task.arguments = newArgv;
task.standardOutput = standardOutput;
task.standardError = standardError;
//task.environment = @{@"DYLD_LIBRARY_PATH": libraryPath.path};
if (self.environment) {
task.environment = self.environment;
}
task.qualityOfService = NSQualityOfServiceUserInitiated;
task.terminationHandler = ^(NSTask *task) {
BOOL normalExit = task.terminationReason == NSTaskTerminationReasonExit && task.terminationStatus == 0;
+2
View File
@@ -21,6 +21,8 @@ NS_ASSUME_NONNULL_BEGIN
// The protocol that this service will vend as its API. This header file will also need to be visible to the process hosting the service.
@protocol QEMUHelperProtocol
@property (nonatomic, nullable) NSDictionary<NSString *, NSString *> *environment;
- (void)accessDataWithBookmark:(NSData *)bookmark securityScoped:(BOOL)securityScoped completion:(void(^)(BOOL, NSData * _Nullable, NSString * _Nullable))completion;
- (void)stopAccessingPath:(nullable NSString *)path;
- (void)startQemu:(NSString *)binName standardOutput:(NSFileHandle *)standardOutput standardError:(NSFileHandle *)standardError libraryBookmark:(NSData *)libBookmark argv:(NSArray<NSString *> *)argv onExit:(void(^)(BOOL,NSString *))onExit;
+5 -6
View File
@@ -68,8 +68,7 @@ static int loadQemu(const char *dylibPath, qemu_main_t *funcs) {
return 0;
}
static void __attribute__((noreturn)) runQemu(qemu_main_t *funcs, int argc, const char **argv) {
const char *envp[] = { NULL };
static void __attribute__((noreturn)) runQemu(qemu_main_t *funcs, int argc, const char **argv, const char **envp) {
if (funcs->qemu_init) {
funcs->qemu_init(argc, argv, envp);
}
@@ -85,7 +84,7 @@ static void __attribute__((noreturn)) runQemu(qemu_main_t *funcs, int argc, cons
exit(0);
}
pid_t startQemuFork(const char *dylibPath, int argc, const char **argv, int newStdout, int newStderr) {
pid_t startQemuFork(const char *dylibPath, int argc, const char **argv, const char **envp, int newStdout, int newStderr) {
qemu_main_t funcs = {};
int res = loadQemu(dylibPath, &funcs);
if (res < 0) {
@@ -103,16 +102,16 @@ pid_t startQemuFork(const char *dylibPath, int argc, const char **argv, int newS
// set thread QoS
pthread_set_qos_class_self_np(QOS_CLASS_USER_INTERACTIVE, 0);
// launch qemu
runQemu(&funcs, argc, argv);
runQemu(&funcs, argc, argv, envp);
}
int startQemuProcess(const char *dylibPath, int argc, const char **argv) {
int startQemuProcess(const char *dylibPath, int argc, const char **argv, const char **envp) {
qemu_main_t funcs = {};
int res = loadQemu(dylibPath, &funcs);
if (res < 0) {
return res;
}
// launch qemu
runQemu(&funcs, argc, argv);
runQemu(&funcs, argc, argv, envp);
return 0;
}
+2 -2
View File
@@ -19,7 +19,7 @@
#include <unistd.h>
pid_t startQemuFork(const char *dylibPath, int argc, const char **argv, int newStdout, int newStderr);
int startQemuProcess(const char *dylibPath, int argc, const char **argv);
pid_t startQemuFork(const char *dylibPath, int argc, const char **argv, const char **envp, int newStdout, int newStderr);
int startQemuProcess(const char *dylibPath, int argc, const char **argv, const char **envp);
#endif /* Bootstrap_h */
+3 -1
View File
@@ -17,10 +17,12 @@
#include <stdio.h>
#include "Bootstrap.h"
extern const char **environ;
int main(int argc, const char * argv[]) {
if (argc < 2) {
fprintf(stderr, "usage: QEMULauncher dylibPath qemuArguments...\n");
return 1;
}
return startQemuProcess(argv[1], argc - 1, &argv[1]);
return startQemuProcess(argv[1], argc - 1, &argv[1], environ);
}