diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a3fa9e77..b71b2b6f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -147,6 +147,9 @@ jobs: if: steps.cache-sysroot-universal.outputs.cache-matched-key == '' run: | ./scripts/pack_dependencies.sh . macos arm64 x86_64 + - name: Install D3DMetal + if: steps.cache-sysroot-universal.outputs.cache-matched-key == '' || github.event_name == 'release' || github.event.inputs.test_release == 'true' || github.event.inputs.rebuild_sysroot == 'true' + run: ./scripts/install_d3dmetal.sh sysroot-macOS-arm64_x86_64 "${{ secrets.D3DM_DIST_DMG_URL || vars.D3DM_DIST_DMG_URL }}" - name: Compress Sysroot if: steps.cache-sysroot-universal.outputs.cache-matched-key == '' || github.event_name == 'release' || github.event.inputs.test_release == 'true' || github.event.inputs.rebuild_sysroot == 'true' run: tar -acf sysroot.tgz sysroot-macOS-arm64_x86_64 diff --git a/AGENTS.md b/AGENTS.md index 3ab5f3bb..baf829ce 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -43,7 +43,7 @@ must apply unprompted — they override default behavior and are easy to violate `Assisted-by: AGENT:MODEL` trailer (e.g. `Assisted-by: Claude:claude-opus-4-8`) — add it explicitly **even if your agent doesn't normally inject attribution trailers**; the absence of a default trailer is not an excuse to omit it. Never - add `Co-authored-by` (strip any a tool adds). Commit titles are + add `Co-authored-by` or session links (strip any a tool adds). Commit titles are `component: short description` explaining *why*. - **Scope:** one feature/fix per PR; don't touch, refactor, or reformat unrelated files; route new logging through `UTMLogging`/`logging` at `debug` level. diff --git a/Build.xcconfig b/Build.xcconfig index 6329f71e..4f7dbb1f 100644 --- a/Build.xcconfig +++ b/Build.xcconfig @@ -39,3 +39,6 @@ LAUNCHER_CODE_SIGN_ENTITLEMENTS = $(LAUNCHER_CODE_SIGN_ENTITLEMENTS_$(DEVELOPER_ CLI_CODE_SIGN_ENTITLEMENTS_YES = utmctl/utmctl.entitlements CLI_CODE_SIGN_ENTITLEMENTS_NO = utmctl/utmctl-unsigned.entitlements CLI_CODE_SIGN_ENTITLEMENTS = $(CLI_CODE_SIGN_ENTITLEMENTS_$(DEVELOPER_ACCOUNT_VM_ACCESS:default=NO)) +RENDERER_CODE_SIGN_ENTITLEMENTS_YES = QEMURenderServer/QEMURenderServer.entitlements +RENDERER_CODE_SIGN_ENTITLEMENTS_NO = QEMURenderServer/QEMURenderServer-unsigned.entitlements +RENDERER_CODE_SIGN_ENTITLEMENTS = $(RENDERER_CODE_SIGN_ENTITLEMENTS_$(DEVELOPER_ACCOUNT_VM_ACCESS:default=NO)) diff --git a/CLAUDE.md b/CLAUDE.md new file mode 120000 index 00000000..47dc3e3d --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +AGENTS.md \ No newline at end of file diff --git a/Configuration/UTMQemuConfiguration+Arguments.swift b/Configuration/UTMQemuConfiguration+Arguments.swift index a42c51a7..36ccfdaf 100644 --- a/Configuration/UTMQemuConfiguration+Arguments.swift +++ b/Configuration/UTMQemuConfiguration+Arguments.swift @@ -27,13 +27,21 @@ import Virtualization // for getting network interfaces private func f(_ string: String = "") -> QEMUArgumentFragment { QEMUArgumentFragment(final: string) } - + + /// Shared group ID for main process and helper + var appGroupIdentifier: String? { + let appGroup = Bundle.main.infoDictionary?["AppGroupIdentifier"] as? String + if let appGroup, !appGroup.hasPrefix("invalid.") { + return appGroup + } + return nil + } + /// Shared between helper and main process to store Unix sockets var socketURL: URL { #if os(iOS) || os(visionOS) return FileManager.default.temporaryDirectory #else - let appGroup = Bundle.main.infoDictionary?["AppGroupIdentifier"] as? String let helper = Bundle.main.infoDictionary?["HelperIdentifier"] as? String // default to unsigned sandbox path var parentURL: URL = FileManager.default.homeDirectoryForCurrentUser @@ -42,7 +50,7 @@ import Virtualization // for getting network interfaces parentURL.appendPathComponent(helper ?? "com.utmapp.QEMUHelper") parentURL.appendPathComponent("Data") parentURL.appendPathComponent("tmp") - if let appGroup = appGroup, !appGroup.hasPrefix("invalid.") { + if let appGroup = appGroupIdentifier { if let containerURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroup) { return containerURL } @@ -276,10 +284,25 @@ import Virtualization // for getting network interfaces "edid=on" } if isDisplayGLSupported(display) && isVulkanSupported { + #if os(macOS) + "hostmem=8G" + #else "hostmem=256M" + #endif "blob=true" "venus=true" } + if isDisplayGLSupported(display) && isNeptuneSupported { + if !isVulkanSupported { + #if os(macOS) + "hostmem=8G" + #else + "hostmem=256M" + #endif + "blob=true" + } + "neptune=true" + } f() } } @@ -322,12 +345,27 @@ import Virtualization // for getting network interfaces return UTMQEMUVulkanDriver(rawValue: rawValue) ?? .qemuVulkanDriverDefault } + private var directXDriver: UTMQEMUDirectXDriver { + let rawValue = UserDefaults.standard.integer(forKey: "QEMUDirectXDriver") + return UTMQEMUDirectXDriver(rawValue: rawValue) ?? .qemuDirectXDriverDefault + } + private var isVulkanSupported: Bool { isGLSupported && (rendererBackend == .qemuRendererBackendAngleMetal || rendererBackend == .qemuRendererBackendDefault) && vulkanDriver != .qemuVulkanDriverDisabled } + private var isNeptuneSupported: Bool { + #if os(macOS) + isGLSupported && + (rendererBackend == .qemuRendererBackendAngleMetal || rendererBackend == .qemuRendererBackendDefault) && + directXDriver != .qemuDirectXDriverDisabled + #else + false + #endif + } + private var isSparc: Bool { system.architecture == .sparc || system.architecture == .sparc64 } diff --git a/Platform/UTMDownloadSupportToolsTask.swift b/Platform/UTMDownloadSupportToolsTask.swift index c7dabfec..1f278283 100644 --- a/Platform/UTMDownloadSupportToolsTask.swift +++ b/Platform/UTMDownloadSupportToolsTask.swift @@ -20,8 +20,8 @@ import Foundation class UTMDownloadSupportToolsTask: UTMDownloadTask { private let vm: any UTMSpiceVirtualMachine - private static let supportToolsDownloadUrl = URL(string: "https://getutm.app/downloads/utm-guest-tools-latest.iso")! - + private static let supportToolsDownloadUrl = URL(string: "https://getutm.app/downloads/utm-guest-tools-3d-latest.iso")! + private var toolsUrl: URL { fileManager.urls(for: .applicationSupportDirectory, in: .userDomainMask).first!.appendingPathComponent("GuestSupportTools") } diff --git a/Platform/iOS/Info.plist b/Platform/iOS/Info.plist index b2aa267b..60a5f1e8 100644 --- a/Platform/iOS/Info.plist +++ b/Platform/iOS/Info.plist @@ -144,5 +144,7 @@ CADisableMinimumFrameDurationOnPhone + AppGroupIdentifier + group.$(PRODUCT_BUNDLE_PREFIX:default=com.utmapp).UTM diff --git a/Platform/iOS/iOS.entitlements b/Platform/iOS/iOS.entitlements index 510563fb..00ab2f17 100644 --- a/Platform/iOS/iOS.entitlements +++ b/Platform/iOS/iOS.entitlements @@ -2,9 +2,13 @@ - com.apple.developer.kernel.increased-memory-limit - com.apple.developer.kernel.extended-virtual-addressing + com.apple.developer.kernel.increased-memory-limit + + com.apple.security.application-groups + + group.$(PRODUCT_BUNDLE_PREFIX:default=com.utmapp).UTM + diff --git a/Platform/macOS/SettingsView.swift b/Platform/macOS/SettingsView.swift index f3846eef..cf84742c 100644 --- a/Platform/macOS/SettingsView.swift +++ b/Platform/macOS/SettingsView.swift @@ -211,12 +211,26 @@ struct DisplaySettingsView: View { @AppStorage("NoSaveScreenshot") var isNoSaveScreenshot = false @AppStorage("QEMURendererBackend") var qemuRendererBackend: UTMQEMURendererBackend = .qemuRendererBackendDefault @AppStorage("QEMUVulkanDriver") var qemuVulkanDriver: UTMQEMUVulkanDriver = .qemuVulkanDriverDefault + @AppStorage("QEMUDirectXDriver") var qemuDirectXDriver: UTMQEMUDirectXDriver = .qemuDirectXDriverDefault @AppStorage("QEMURendererFPSLimit") var qemuRendererFpsLimit: Int = 0 private var isVulkanSupported: Bool { qemuRendererBackend == .qemuRendererBackendDefault || qemuRendererBackend == .qemuRendererBackendAngleMetal } + private static var hasD3DMetal: Bool = { + let d3dMetalFramework = Bundle.main.bundleURL.appendingPathComponent("Contents/Frameworks/D3DMetal.framework") + return FileManager.default.fileExists(atPath: d3dMetalFramework.path) + }() + + private var isD3DMetalAvailable: Bool { + if qemuDirectXDriver == .qemuDirectXDriverD3DMetal { + return true // always show option if already selected + } else { + return Self.hasD3DMetal + } + } + var body: some View { Form { Section(header: Text("Display")) { @@ -253,6 +267,20 @@ struct DisplaySettingsView: View { qemuVulkanDriver = .qemuVulkanDriverDefault } } + Picker("DirectX Driver", selection: $qemuDirectXDriver) { + Text("Default").tag(UTMQEMUDirectXDriver.qemuDirectXDriverDefault) + Text("Disabled").tag(UTMQEMUDirectXDriver.qemuDirectXDriverDisabled) + Text("DXMT").tag(UTMQEMUDirectXDriver.qemuDirectXDriverDXMT) + if isD3DMetalAvailable { + Text("D3DMetal").tag(UTMQEMUDirectXDriver.qemuDirectXDriverD3DMetal) + } + }.help("Select the DirectX driver to use for host passthrough rendering. DirectX requires guest drivers to be installed.") + .disabled(!isVulkanSupported) + .onChange(of: qemuRendererBackend) { _ in + if !isVulkanSupported { + qemuDirectXDriver = .qemuDirectXDriverDefault + } + } if !isVulkanSupported { Text("The selected renderer backend does not support Vulkan.") } diff --git a/QEMUHelper/QEMUHelper.m b/QEMUHelper/QEMUHelper.m index 2ac2b5a2..8732be81 100644 --- a/QEMUHelper/QEMUHelper.m +++ b/QEMUHelper/QEMUHelper.m @@ -128,7 +128,9 @@ static const int64_t kProcessTerminateTimeoutSeconds = 1; task.standardOutput = standardOutput; task.standardError = standardError; NSMutableDictionary *environment = [NSMutableDictionary dictionary]; + NSURL *renderServerURL = [[[NSBundle mainBundle] URLForAuxiliaryExecutable:@"QEMURenderServer.app"] URLByAppendingPathComponent:@"Contents/MacOS/QEMURenderServer"]; environment[@"TMPDIR"] = NSFileManager.defaultManager.temporaryDirectory.path; + environment[@"RENDER_SERVER_EXEC_PATH"] = renderServerURL.path; if (self.environment) { [environment addEntriesFromDictionary:self.environment]; } diff --git a/QEMURenderServer/QEMURenderServer-unsigned.entitlements b/QEMURenderServer/QEMURenderServer-unsigned.entitlements new file mode 100644 index 00000000..f1a60e82 --- /dev/null +++ b/QEMURenderServer/QEMURenderServer-unsigned.entitlements @@ -0,0 +1,12 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.cs.disable-library-validation + + com.apple.security.inherit + + + diff --git a/QEMURenderServer/QEMURenderServer.entitlements b/QEMURenderServer/QEMURenderServer.entitlements new file mode 100644 index 00000000..794eada1 --- /dev/null +++ b/QEMURenderServer/QEMURenderServer.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.inherit + + + diff --git a/Services/UTMQemuSystem.h b/Services/UTMQemuSystem.h index 4cbc6a45..061e9194 100644 --- a/Services/UTMQemuSystem.h +++ b/Services/UTMQemuSystem.h @@ -26,7 +26,9 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, nullable) NSDictionary *remoteBookmarks; @property (nonatomic) UTMQEMURendererBackend rendererBackend; @property (nonatomic) UTMQEMUVulkanDriver vulkanDriver; +@property (nonatomic) UTMQEMUDirectXDriver directXDriver; @property (nonatomic) NSURL *shmemDirectoryURL; +@property (nonatomic, nullable) NSString *appSandboxGroupId; @property (nonatomic, weak) id launcherDelegate; @property (nonatomic, nullable) QEMULogging *logging; @property (nonatomic) BOOL hasDebugLog; diff --git a/Services/UTMQemuSystem.m b/Services/UTMQemuSystem.m index 1f414b84..008f8649 100644 --- a/Services/UTMQemuSystem.m +++ b/Services/UTMQemuSystem.m @@ -58,7 +58,6 @@ static int startQemu(UTMProcess *process, int argc, const char *argv[], const ch } - (void)setVulkanDriver:(UTMQEMUVulkanDriver)vulkanDriver { - _vulkanDriver = vulkanDriver; NSURL *vulkanIcds = [[NSBundle.mainBundle URLForResource:@"vulkan" withExtension:nil] URLByAppendingPathComponent:@"icd.d" isDirectory:YES]; NSURL *driver; switch (vulkanDriver) { @@ -77,12 +76,86 @@ static int startQemu(UTMProcess *process, int argc, const char *argv[], const ch if (driver) { self.mutableEnvironment[@"VK_DRIVER_FILES"] = driver.path; self.resources = [self.resources arrayByAddingObject:driver]; + _vulkanDriver = vulkanDriver; + } else { + [self.mutableEnvironment removeObjectForKey:@"VK_DRIVER_FILES"]; + _vulkanDriver = kQEMUVulkanDriverDisabled; + } +} + +- (void)setDirectXDriver:(UTMQEMUDirectXDriver)directXDriver { + NSString *backend; + NSString *frameworkName; + NSURL *library; + switch (directXDriver) { + case kQEMUDirectXDriverDefault: + case kQEMUDirectXDriverDXMT: + backend = @"dxmt"; + frameworkName = @"dxmt-native"; + break; + case kQEMUDirectXDriverD3DMetal: + backend = @"d3dmetal"; + frameworkName = @"d3dmetal-native"; + break; + case kQEMUDirectXDriverDisabled: + default: + break; + } + if (frameworkName) { + NSURL *bundleURL = NSBundle.mainBundle.bundleURL; +#if TARGET_OS_OSX + NSURL *contentsURL = [bundleURL URLByAppendingPathComponent:@"Contents" isDirectory:YES]; + NSString *versionPath = @"Versions/A/"; +#else + NSURL *contentsURL = bundleURL; + NSString *versionPath = @""; +#endif + NSURL *frameworksURL = [contentsURL URLByAppendingPathComponent:@"Frameworks" isDirectory:YES]; + library = [frameworksURL URLByAppendingPathComponent:[NSString stringWithFormat:@"%@.framework/%@%@", frameworkName, versionPath, frameworkName] isDirectory:NO]; + if (![NSFileManager.defaultManager fileExistsAtPath:library.path]) { + UTMLog(@"DirectX driver '%@' is not available in this build", backend); + backend = nil; + library = nil; + } else if (directXDriver == kQEMUDirectXDriverD3DMetal) { + NSString *d3dMetal = @"D3DMetal"; + NSURL *d3dMetalURL = [frameworksURL URLByAppendingPathComponent:[NSString stringWithFormat:@"%@.framework/%@%@", d3dMetal, versionPath, d3dMetal] isDirectory:NO]; + self.mutableEnvironment[@"D3DMETAL_FRAMEWORK_PATH"] = d3dMetalURL.path; + self.resources = [self.resources arrayByAddingObject:d3dMetalURL]; + } + } + if (backend && library) { + self.mutableEnvironment[@"NPT_D3D11_LIBRARY_PATH"] = library.path; + self.mutableEnvironment[@"NPT_D3D12_LIBRARY_PATH"] = library.path; + self.mutableEnvironment[@"NPT_DXGI_LIBRARY_PATH"] = library.path; + self.mutableEnvironment[@"NPT_BACKEND"] = backend; + self.resources = [self.resources arrayByAddingObject:library]; + _directXDriver = directXDriver; + } else { + [self.mutableEnvironment removeObjectForKey:@"NPT_D3D11_LIBRARY_PATH"]; + [self.mutableEnvironment removeObjectForKey:@"NPT_D3D12_LIBRARY_PATH"]; + [self.mutableEnvironment removeObjectForKey:@"NPT_DXGI_LIBRARY_PATH"]; + [self.mutableEnvironment removeObjectForKey:@"NPT_BACKEND"]; + [self.mutableEnvironment removeObjectForKey:@"D3DMETAL_FRAMEWORK_PATH"]; + _directXDriver = kQEMUDirectXDriverDisabled; } } - (void)setShmemDirectoryURL:(NSURL *)shmemDirectoryURL { _shmemDirectoryURL = shmemDirectoryURL; - self.mutableEnvironment[@"XDG_RUNTIME_DIR"] = shmemDirectoryURL.path; + if (shmemDirectoryURL) { + self.mutableEnvironment[@"XDG_RUNTIME_DIR"] = shmemDirectoryURL.path; + } else { + [self.mutableEnvironment removeObjectForKey:@"XDG_RUNTIME_DIR"]; + } +} + +- (void)setAppSandboxGroupId:(NSString *)appSandboxGroupId { + _appSandboxGroupId = appSandboxGroupId; + if (appSandboxGroupId) { + self.mutableEnvironment[@"APP_SANDBOX_GROUP_ID"] = appSandboxGroupId; + } else { + [self.mutableEnvironment removeObjectForKey:@"APP_SANDBOX_GROUP_ID"]; + } } - (NSPipe *)standardOutput { @@ -122,6 +195,9 @@ static int startQemu(UTMProcess *process, int argc, const char *argv[], const ch self.mutableEnvironment[@"MVK_CONFIG_LOG_LEVEL"] = @"4"; self.mutableEnvironment[@"MVK_DEBUG"] = @"1"; self.mutableEnvironment[@"MTL_DEBUG_LAYER"] = @"1"; + self.mutableEnvironment[@"MTL_DEBUG_LAYER_ERROR_MODE"] = @"nslog"; + self.mutableEnvironment[@"ANGLE_ENABLE_DEBUG_TRACE"] = @"1"; + self.mutableEnvironment[@"ANGLE_METAL_DEBUG_BINDINGS"] = @"1"; } else { [self.mutableEnvironment removeObjectForKey:@"G_MESSAGES_DEBUG"]; [self.mutableEnvironment removeObjectForKey:@"VK_LOADER_DEBUG"]; @@ -130,6 +206,9 @@ static int startQemu(UTMProcess *process, int argc, const char *argv[], const ch [self.mutableEnvironment removeObjectForKey:@"MVK_CONFIG_LOG_LEVEL"]; [self.mutableEnvironment removeObjectForKey:@"MVK_DEBUG"]; [self.mutableEnvironment removeObjectForKey:@"MTL_DEBUG_LAYER"]; + [self.mutableEnvironment removeObjectForKey:@"MTL_DEBUG_LAYER_ERROR_MODE"]; + [self.mutableEnvironment removeObjectForKey:@"ANGLE_ENABLE_DEBUG_TRACE"]; + [self.mutableEnvironment removeObjectForKey:@"ANGLE_METAL_DEBUG_BINDINGS"]; } } diff --git a/Services/UTMQemuSystemBackends.h b/Services/UTMQemuSystemBackends.h index 9958ee41..db6eb716 100644 --- a/Services/UTMQemuSystemBackends.h +++ b/Services/UTMQemuSystemBackends.h @@ -42,4 +42,12 @@ typedef NS_ENUM(NSInteger, UTMQEMUVulkanDriver) { kQEMUVulkanDriverKosmicKrisp = 3, }; +/// Specify the DirectX driver for this VM +typedef NS_ENUM(NSInteger, UTMQEMUDirectXDriver) { + kQEMUDirectXDriverDefault = 0, + kQEMUDirectXDriverDisabled = 1, + kQEMUDirectXDriverDXMT = 2, + kQEMUDirectXDriverD3DMetal = 3, +}; + #endif /* UTMQemuSystemBackends_h */ diff --git a/Services/UTMQemuVirtualMachine.swift b/Services/UTMQemuVirtualMachine.swift index e5a62566..40df926e 100644 --- a/Services/UTMQemuVirtualMachine.swift +++ b/Services/UTMQemuVirtualMachine.swift @@ -254,6 +254,13 @@ extension UTMQemuVirtualMachine { } } + private var directXDriver: UTMQEMUDirectXDriver { + get { + let rawValue = UserDefaults.standard.integer(forKey: "QEMUDirectXDriver") + return UTMQEMUDirectXDriver(rawValue: rawValue) ?? .qemuDirectXDriverDefault + } + } + @MainActor private func qemuEnsureEfiVarsAvailable() async throws { guard let efiVarsURL = config.qemu.efiVarsURL else { return @@ -346,7 +353,9 @@ extension UTMQemuVirtualMachine { system.remoteBookmarks = remoteBookmarks system.rendererBackend = rendererBackend system.vulkanDriver = try vulkanDriver + system.directXDriver = directXDriver system.shmemDirectoryURL = await config.shmemDirectoryURL + system.appSandboxGroupId = await config.appGroupIdentifier system.hasDebugLog = hasDebugLog try Task.checkCancellation() diff --git a/UTM.xcodeproj/project.pbxproj b/UTM.xcodeproj/project.pbxproj index 1a37efbb..e160d35b 100644 --- a/UTM.xcodeproj/project.pbxproj +++ b/UTM.xcodeproj/project.pbxproj @@ -52,7 +52,7 @@ 84018699288B71BF0050AC51 /* BusyIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84018696288B71BF0050AC51 /* BusyIndicator.swift */; }; 8401FD71269BEB2B00265F0D /* main.c in Sources */ = {isa = PBXBuildFile; fileRef = CE6B240A25F1F3CE0020D43E /* main.c */; }; 8401FD72269BEB3000265F0D /* Bootstrap.c in Sources */ = {isa = PBXBuildFile; fileRef = CE0DF17125A80B6300A51894 /* Bootstrap.c */; }; - 8401FD7A269BECE200265F0D /* QEMULauncher.app in Embed Launcher */ = {isa = PBXBuildFile; fileRef = 8401FD62269BE9C500265F0D /* QEMULauncher.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 8401FD7A269BECE200265F0D /* QEMULauncher.app in Embed Executables */ = {isa = PBXBuildFile; fileRef = 8401FD62269BE9C500265F0D /* QEMULauncher.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 8401FDA0269D266E00265F0D /* VMConfigAppleBootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8401FD9F269D266E00265F0D /* VMConfigAppleBootView.swift */; }; 8401FDA2269D3E2500265F0D /* VMConfigAppleNetworkingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8401FDA1269D3E2500265F0D /* VMConfigAppleNetworkingView.swift */; }; 8401FDA4269D43CF00265F0D /* VMConfigAppleDisplayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8401FDA3269D43CF00265F0D /* VMConfigAppleDisplayView.swift */; }; @@ -735,6 +735,11 @@ CE9B15482B12A87E003A32DD /* GenerateKey.c in Sources */ = {isa = PBXBuildFile; fileRef = CE9B15462B12A87E003A32DD /* GenerateKey.c */; }; CE9B15492B12A87E003A32DD /* GenerateKey.c in Sources */ = {isa = PBXBuildFile; fileRef = CE9B15462B12A87E003A32DD /* GenerateKey.c */; }; CE9B154A2B12A87E003A32DD /* GenerateKey.c in Sources */ = {isa = PBXBuildFile; fileRef = CE9B15462B12A87E003A32DD /* GenerateKey.c */; }; + CEA3EDEB3016810500AAA8E4 /* dxmt-native.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEA3EDE93016810500AAA8E4 /* dxmt-native.framework */; }; + CEA3EDEC3016810500AAA8E4 /* dxmt-native.framework in Embed Libraries */ = {isa = PBXBuildFile; fileRef = CEA3EDE93016810500AAA8E4 /* dxmt-native.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + CEA3EDED3016810500AAA8E4 /* d3dmetal-native.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEA3EDEA3016810500AAA8E4 /* d3dmetal-native.framework */; }; + CEA3EDEE3016810500AAA8E4 /* d3dmetal-native.framework in Embed Libraries */ = {isa = PBXBuildFile; fileRef = CEA3EDEA3016810500AAA8E4 /* d3dmetal-native.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + CEA3EE05301691C500AAA8E4 /* QEMURenderServer.app in Embed Executables */ = {isa = PBXBuildFile; fileRef = CEA3EDFA30168B9000AAA8E4 /* QEMURenderServer.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; CEA45E25263519B5002FA97D /* VMDisplayMetalViewController+Pointer.h in Sources */ = {isa = PBXBuildFile; fileRef = 83FBDD53242FA71900D2C5D7 /* VMDisplayMetalViewController+Pointer.h */; }; CEA45E27263519B5002FA97D /* VMRemovableDrivesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE2D954524AD4F980059923A /* VMRemovableDrivesView.swift */; }; CEA45E37263519B5002FA97D /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE772AAB25C8B0F600E4E379 /* ContentView.swift */; }; @@ -1313,6 +1318,13 @@ remoteGlobalIDString = CE9A352C26533A51005077CF; remoteInfo = JailbreakInterposer; }; + CEA3EE03301691C500AAA8E4 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = CE550BC1225947990063E575 /* Project object */; + proxyType = 1; + remoteGlobalIDString = CEA3EDEF30168B9000AAA8E4; + remoteInfo = QEMURenderServer; + }; CEBDA1E324D8BDDB0010B5EC /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = CE550BC1225947990063E575 /* Project object */; @@ -1364,6 +1376,7 @@ CEF83F8F250094EE00557D15 /* gcrypt.20.framework in Embed Libraries */, CE0B6F5424AD67FA00FE012D /* spice-client-glib-2.0.8.framework in Embed Libraries */, CEF83F8B250094D700557D15 /* spice-server.1.framework in Embed Libraries */, + CEA3EDEC3016810500AAA8E4 /* dxmt-native.framework in Embed Libraries */, CE0B6F1B24AD679700FE012D /* gstapp-1.0.0.framework in Embed Libraries */, CE0B6F2924AD67AD00FE012D /* gsttag-1.0.0.framework in Embed Libraries */, CE0B6F3124AD67C100FE012D /* phodav-3.0.0.framework in Embed Libraries */, @@ -1388,6 +1401,7 @@ CE03D0C524D913AF00F76B84 /* ffi.8.framework in Embed Libraries */, CEF83F8D250094E700557D15 /* gthread-2.0.0.framework in Embed Libraries */, CE63B0DB2F0ED4FF00C59D13 /* vulkan_kosmickrisp.framework in Embed Libraries */, + CEA3EDEE3016810500AAA8E4 /* d3dmetal-native.framework in Embed Libraries */, CE064C662A563F4B003C833D /* swtpm.0.framework in Embed Libraries */, CE03D09024D9124800F76B84 /* intl.8.framework in Embed Libraries */, CE03D0C924D9140A00F76B84 /* pixman-1.0.framework in Embed Libraries */, @@ -1521,15 +1535,16 @@ name = "Embed Libraries"; runOnlyForDeploymentPostprocessing = 0; }; - CE6B241425F1F5630020D43E /* Embed Launcher */ = { + CE6B241425F1F5630020D43E /* Embed Executables */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 6; files = ( - 8401FD7A269BECE200265F0D /* QEMULauncher.app in Embed Launcher */, + 8401FD7A269BECE200265F0D /* QEMULauncher.app in Embed Executables */, + CEA3EE05301691C500AAA8E4 /* QEMURenderServer.app in Embed Executables */, ); - name = "Embed Launcher"; + name = "Embed Executables"; runOnlyForDeploymentPostprocessing = 0; }; CEA45F71263519B5002FA97D /* Embed Libraries */ = { @@ -2075,6 +2090,11 @@ CE9D197B226542FE00355E14 /* UTMProcess.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = UTMProcess.m; sourceTree = ""; }; CEA02A972436C7A30087E45F /* UTMLegacyQemuConfiguration+Networking.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UTMLegacyQemuConfiguration+Networking.h"; sourceTree = ""; }; CEA02A982436C7A30087E45F /* UTMLegacyQemuConfiguration+Networking.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UTMLegacyQemuConfiguration+Networking.m"; sourceTree = ""; }; + CEA3EDE93016810500AAA8E4 /* dxmt-native.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = "dxmt-native.framework"; path = "$(SYSROOT_DIR)/Frameworks/dxmt-native.framework"; sourceTree = ""; }; + CEA3EDEA3016810500AAA8E4 /* d3dmetal-native.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = "d3dmetal-native.framework"; path = "$(SYSROOT_DIR)/Frameworks/d3dmetal-native.framework"; sourceTree = ""; }; + CEA3EDFA30168B9000AAA8E4 /* QEMURenderServer.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = QEMURenderServer.app; sourceTree = BUILT_PRODUCTS_DIR; }; + CEA3EDFD30168C9900AAA8E4 /* QEMURenderServer.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = QEMURenderServer.entitlements; sourceTree = ""; }; + CEA3EE13301C15DD00AAA8E4 /* QEMURenderServer-unsigned.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "QEMURenderServer-unsigned.entitlements"; sourceTree = ""; }; CEA45FB9263519B5002FA97D /* UTM SE.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "UTM SE.app"; sourceTree = BUILT_PRODUCTS_DIR; }; CEA51F862A81EAB700DDD7FA /* VMToolbarOrnamentModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VMToolbarOrnamentModifier.swift; sourceTree = ""; }; CEA9053725F981E900801E7C /* usb-1.0.0.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = "usb-1.0.0.framework"; path = "$(SYSROOT_DIR)/Frameworks/usb-1.0.0.framework"; sourceTree = ""; }; @@ -2349,6 +2369,7 @@ CEF83F882500949D00557D15 /* gthread-2.0.0.framework in Frameworks */, CE03D08724D90F0700F76B84 /* gobject-2.0.0.framework in Frameworks */, CE9B15362B11A491003A32DD /* SwiftConnect in Frameworks */, + CEA3EDED3016810500AAA8E4 /* d3dmetal-native.framework in Frameworks */, CE0B6F0A24AD677200FE012D /* spice-client-glib-2.0.8.framework in Frameworks */, CE0B6ECF24AD677200FE012D /* gstrtp-1.0.0.framework in Frameworks */, CE0B6ECC24AD677200FE012D /* gstriff-1.0.0.framework in Frameworks */, @@ -2364,6 +2385,7 @@ CE0B6EBB24AD677200FE012D /* libgstgio.a in Frameworks */, 84937EFF28960789003148F4 /* zstd.1.framework in Frameworks */, CE0B6EE224AD677200FE012D /* gstnet-1.0.0.framework in Frameworks */, + CEA3EDEB3016810500AAA8E4 /* dxmt-native.framework in Frameworks */, CE03D08624D90F0700F76B84 /* gmodule-2.0.0.framework in Frameworks */, CED297142CE425CB00F1E3EB /* soup-3.0.0.framework in Frameworks */, CE03D0CA24D9142000F76B84 /* ssl.1.1.framework in Frameworks */, @@ -2624,6 +2646,8 @@ CE2D63D622653C7300FC7E63 /* Frameworks */ = { isa = PBXGroup; children = ( + CEA3EDEA3016810500AAA8E4 /* d3dmetal-native.framework */, + CEA3EDE93016810500AAA8E4 /* dxmt-native.framework */, CE63B0D92F0ED4FF00C59D13 /* MoltenVK.framework */, CE63B0D72F0ED4FE00C59D13 /* vulkan_kosmickrisp.framework */, CE63B0D82F0ED4FE00C59D13 /* vulkan.1.framework */, @@ -2894,6 +2918,7 @@ CE9D18F72265410E00355E14 /* qemu */, CE63B0F02F0ED67700C59D13 /* vulkan */, CE6B240925F1F3CE0020D43E /* QEMULauncher */, + CEA3EDFB30168C6300AAA8E4 /* QEMURenderServer */, CE9A352E26533A51005077CF /* JailbreakInterposer */, CE9B153D2B11A4ED003A32DD /* Remote */, CE4698F824C8FBD9008C1BD6 /* Icons */, @@ -2917,6 +2942,7 @@ 8401FD62269BE9C500265F0D /* QEMULauncher.app */, 84E3A8F0293DB37E0024A740 /* utmctl */, CEF7F6D32AEEDCC400E34952 /* UTM Remote.app */, + CEA3EDFA30168B9000AAA8E4 /* QEMURenderServer.app */, ); name = Products; sourceTree = ""; @@ -3047,6 +3073,15 @@ path = Remote; sourceTree = ""; }; + CEA3EDFB30168C6300AAA8E4 /* QEMURenderServer */ = { + isa = PBXGroup; + children = ( + CEA3EDFD30168C9900AAA8E4 /* QEMURenderServer.entitlements */, + CEA3EE13301C15DD00AAA8E4 /* QEMURenderServer-unsigned.entitlements */, + ); + path = QEMURenderServer; + sourceTree = ""; + }; CEB63A9624F47C1200CAF323 /* Shared */ = { isa = PBXGroup; children = ( @@ -3249,6 +3284,7 @@ CE2D951924AD48BE0059923A /* Frameworks */, CE2D951A24AD48BE0059923A /* Resources */, CE0B6E6D24AD66CE00FE012D /* Embed Libraries */, + CEA3EE0F301A5A4900AAA8E4 /* Embed D3DMetal */, CEBDA1E924D8BDDB0010B5EC /* Embed XPC Services */, 84E3A8FA293DB70A0024A740 /* Embed CLI Tool */, ); @@ -3293,6 +3329,21 @@ productReference = CE9A352D26533A51005077CF /* JailbreakInterposer.framework */; productType = "com.apple.product-type.framework"; }; + CEA3EDEF30168B9000AAA8E4 /* QEMURenderServer */ = { + isa = PBXNativeTarget; + buildConfigurationList = CEA3EDF730168B9000AAA8E4 /* Build configuration list for PBXNativeTarget "QEMURenderServer" */; + buildPhases = ( + CEA3EE0130168DF000AAA8E4 /* Copy prebuilt binary */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = QEMURenderServer; + productName = QEMULauncher; + productReference = CEA3EDFA30168B9000AAA8E4 /* QEMURenderServer.app */; + productType = "com.apple.product-type.application"; + }; CEA45E1F263519B5002FA97D /* iOS-SE */ = { isa = PBXNativeTarget; buildConfigurationList = CEA45FB6263519B5002FA97D /* Build configuration list for PBXNativeTarget "iOS-SE" */; @@ -3333,12 +3384,13 @@ CEBDA1D624D8BDDA0010B5EC /* Sources */, CEBDA1D724D8BDDA0010B5EC /* Frameworks */, CEBDA1D824D8BDDA0010B5EC /* Resources */, - CE6B241425F1F5630020D43E /* Embed Launcher */, + CE6B241425F1F5630020D43E /* Embed Executables */, ); buildRules = ( ); dependencies = ( 8401FD7C269BECF200265F0D /* PBXTargetDependency */, + CEA3EE04301691C500AAA8E4 /* PBXTargetDependency */, ); name = QEMUHelper; productName = QEMUHelper; @@ -3457,6 +3509,7 @@ CE2D951B24AD48BE0059923A /* macOS */, CEBDA1D924D8BDDA0010B5EC /* QEMUHelper */, 8401FD61269BE9C500265F0D /* QEMULauncher */, + CEA3EDEF30168B9000AAA8E4 /* QEMURenderServer */, CE9A352C26533A51005077CF /* JailbreakInterposer */, 84E3A8EF293DB37E0024A740 /* utmctl */, ); @@ -3625,6 +3678,46 @@ shellScript = "#!/bin/sh\n\nPLISTBUDDY=/usr/libexec/PlistBuddy\nROOT_PLIST=\"$SCRIPT_OUTPUT_FILE_0\"\n\nCOUNT=$($PLISTBUDDY -c \"Print :PreferenceSpecifiers\" \"$ROOT_PLIST\" | grep -c '^ }$')\n\nfor ((i = 0; i < COUNT; i++)); do\n remove=0\n platform=$($PLISTBUDDY -c \"Print :PreferenceSpecifiers:$i:Platform\" \"$ROOT_PLIST\" 2> /dev/null)\n if [ ! -z \"$platform\" ]; then\n if [ \"$platform\" == \"$PLATFORM_FAMILY_NAME\" ]; then\n echo \"Found entry $i for $platform\"\n $PLISTBUDDY -c \"Delete :PreferenceSpecifiers:$i:Platform\" \"$ROOT_PLIST\"\n else\n echo \"Exclude $i due to Platform\"\n remove=1\n fi\n fi\n excludetargets=$($PLISTBUDDY -c \"Print :PreferenceSpecifiers:$i:ExcludeTargets\" \"$ROOT_PLIST\" 2> /dev/null | sed '1d;$d' | xargs)\n if [ ! -z \"$excludetargets\" ]; then\n found=0\n for target in $excludetargets; do\n if [ \"$target\" == \"$TARGET_NAME\" ]; then\n found=1\n fi\n done\n if [ $found -eq 1 ]; then\n echo \"Exclude $i due to ExcludeTargets\"\n remove=1\n else\n echo \"Found entry $i for ExcludeTargets\"\n $PLISTBUDDY -c \"Delete :PreferenceSpecifiers:$i:ExcludeTargets\" \"$ROOT_PLIST\"\n fi\n fi\n if [ $remove -eq 1 ]; then\n echo \"Removing entry $i\"\n $PLISTBUDDY -c \"Delete :PreferenceSpecifiers:$i\" \"$ROOT_PLIST\"\n ((COUNT--))\n ((i--))\n fi\ndone\n"; showEnvVarsInLog = 0; }; + CEA3EE0130168DF000AAA8E4 /* Copy prebuilt binary */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "$(PROJECT_DIR)/$(SYSROOT_DIR)/libexec/virgl_render_server", + ); + name = "Copy prebuilt binary"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(TARGET_BUILD_DIR)/$(EXECUTABLE_PATH)", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "set -e\n\nSRC=\"${PROJECT_DIR}/${SYSROOT_DIR}/libexec/virgl_render_server\"\nDST=\"${TARGET_BUILD_DIR}/${EXECUTABLE_PATH}\"\nTMP=\"${TARGET_TEMP_DIR}/${EXECUTABLE_NAME}\"\n\nif [ ! -f \"${SRC}\" ]; then\n echo \"error: prebuilt binary not found at ${SRC}\" >&2\n exit 1\nfi\n\n# install_name_tool writes a temporary file next to the binary it patches,\n# which the script sandbox disallows inside the product, so patch it here.\nrm -f \"${TMP}\"\ncp -X \"${SRC}\" \"${TMP}\"\nxattr -c \"${TMP}\"\nchmod +x \"${TMP}\"\n\n# The prebuilt binary has no LC_RPATH but links against\n# @rpath/vulkan.1.framework, so add the load paths this target declares.\nfor RPATH in ${LD_RUNPATH_SEARCH_PATHS}; do\n install_name_tool -add_rpath \"${RPATH}\" \"${TMP}\"\ndone\n\nmkdir -p \"$(dirname \"${DST}\")\"\nrm -f \"${DST}\"\ncp -X \"${TMP}\" \"${DST}\"\n"; + }; + CEA3EE0F301A5A4900AAA8E4 /* Embed D3DMetal */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "$(PROJECT_DIR)/$(SYSROOT_DIR)/Frameworks/D3DMetal.framework", + ); + name = "Embed D3DMetal"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(TARGET_BUILD_DIR)/$(FRAMEWORKS_FOLDER_PATH)/D3DMetal.framework", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "set -e\n\nSRC=\"${PROJECT_DIR}/${SYSROOT_DIR}/Frameworks/D3DMetal.framework\"\nDST=\"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/D3DMetal.framework\"\n\nif [ ! -d \"${SRC}\" ]; then\n echo \"note: D3DMetal.framework not found in ${SYSROOT_DIR}, skipping\"\n rm -rf \"${DST}\"\n exit 0\nfi\n\n# Extended attributes on the input would make codesign refuse the bundle.\nmkdir -p \"$(dirname \"${DST}\")\"\nrm -rf \"${DST}\"\nditto --norsrc --noextattr --noqtn \"${SRC}\" \"${DST}\"\n\nif [ \"${CODE_SIGNING_ALLOWED}\" != \"NO\" ] && [ -n \"${EXPANDED_CODE_SIGN_IDENTITY}\" ]; then\n if [ \"${ENABLE_HARDENED_RUNTIME}\" = \"YES\" ]; then\n RUNTIME_FLAGS=\"--options runtime\"\n fi\n codesign --force --sign \"${EXPANDED_CODE_SIGN_IDENTITY}\" ${RUNTIME_FLAGS} ${OTHER_CODE_SIGN_FLAGS} \"${DST}\"\nfi\n"; + }; CEF7F6812AEEDCC400E34952 /* Patch Settings bundle */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -4426,6 +4519,11 @@ target = CE9A352C26533A51005077CF /* JailbreakInterposer */; targetProxy = CE9A353226533A52005077CF /* PBXContainerItemProxy */; }; + CEA3EE04301691C500AAA8E4 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = CEA3EDEF30168B9000AAA8E4 /* QEMURenderServer */; + targetProxy = CEA3EE03301691C500AAA8E4 /* PBXContainerItemProxy */; + }; CEBDA1E424D8BDDB0010B5EC /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = CEBDA1D924D8BDDA0010B5EC /* QEMUHelper */; @@ -4633,7 +4731,6 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = "$(LAUNCHER_CODE_SIGN_ENTITLEMENTS)"; CODE_SIGN_IDENTITY = "$(CODE_SIGN_IDENTITY_MAC:default=-)"; - CODE_SIGN_INJECT_BASE_ENTITLEMENTS = NO; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; GENERATE_INFOPLIST_FILE = YES; @@ -4655,7 +4752,6 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = "$(LAUNCHER_CODE_SIGN_ENTITLEMENTS)"; CODE_SIGN_IDENTITY = "$(CODE_SIGN_IDENTITY_MAC:default=-)"; - CODE_SIGN_INJECT_BASE_ENTITLEMENTS = NO; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; GENERATE_INFOPLIST_FILE = YES; @@ -4798,6 +4894,7 @@ CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; ENABLE_PREVIEWS = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; GCC_PREPROCESSOR_DEFINITIONS = ( "WITH_JIT=1", "WITH_SERVER=1", @@ -4832,6 +4929,7 @@ CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; ENABLE_PREVIEWS = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; GCC_PREPROCESSOR_DEFINITIONS = ( "WITH_JIT=1", "WITH_SERVER=1", @@ -4925,13 +5023,13 @@ "$(PROJECT_DIR)/$(QUOTED_SYSROOT_DIR)/include/spice-1", "$(PROJECT_DIR)/$(QUOTED_SYSROOT_DIR)/include/libusb-1.0", ); - IPHONEOS_DEPLOYMENT_TARGET = 14.0; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; LIBRARY_SEARCH_PATHS = ( "$(PROJECT_DIR)/$(QUOTED_SYSROOT_DIR)/lib", "$(PROJECT_DIR)/$(QUOTED_SYSROOT_DIR)/lib/gstreamer-1.0", "$(PROJECT_DIR)/$(QUOTED_SYSROOT_DIR)/lib", ); - MACOSX_DEPLOYMENT_TARGET = 11.3; + MACOSX_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; @@ -5008,13 +5106,13 @@ "$(PROJECT_DIR)/$(QUOTED_SYSROOT_DIR)/include/spice-1", "$(PROJECT_DIR)/$(QUOTED_SYSROOT_DIR)/include/libusb-1.0", ); - IPHONEOS_DEPLOYMENT_TARGET = 14.0; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; LIBRARY_SEARCH_PATHS = ( "$(PROJECT_DIR)/$(QUOTED_SYSROOT_DIR)/lib", "$(PROJECT_DIR)/$(QUOTED_SYSROOT_DIR)/lib/gstreamer-1.0", "$(PROJECT_DIR)/$(QUOTED_SYSROOT_DIR)/lib", ); - MACOSX_DEPLOYMENT_TARGET = 11.3; + MACOSX_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; QUOTED_SYSROOT_DIR = "\"$(SYSROOT_DIR)\""; @@ -5080,6 +5178,46 @@ }; name = Release; }; + CEA3EDF830168B9000AAA8E4 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = "$(RENDERER_CODE_SIGN_ENTITLEMENTS)"; + CODE_SIGN_IDENTITY = "$(CODE_SIGN_IDENTITY_MAC:default=-)"; + CODE_SIGN_STYLE = Manual; + COMBINE_HIDPI_IMAGES = YES; + GENERATE_INFOPLIST_FILE = YES; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../../../../../../../Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "$(PRODUCT_BUNDLE_PREFIX:default=com.utmapp).QEMURenderServer"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = macosx; + SWIFT_EMIT_LOC_STRINGS = YES; + }; + name = Debug; + }; + CEA3EDF930168B9000AAA8E4 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = "$(RENDERER_CODE_SIGN_ENTITLEMENTS)"; + CODE_SIGN_IDENTITY = "$(CODE_SIGN_IDENTITY_MAC:default=-)"; + CODE_SIGN_STYLE = Manual; + COMBINE_HIDPI_IMAGES = YES; + GENERATE_INFOPLIST_FILE = YES; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../../../../../../../Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "$(PRODUCT_BUNDLE_PREFIX:default=com.utmapp).QEMURenderServer"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = macosx; + SWIFT_EMIT_LOC_STRINGS = YES; + }; + name = Release; + }; CEA45FB7263519B5002FA97D /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -5327,6 +5465,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + CEA3EDF730168B9000AAA8E4 /* Build configuration list for PBXNativeTarget "QEMURenderServer" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + CEA3EDF830168B9000AAA8E4 /* Debug */, + CEA3EDF930168B9000AAA8E4 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; CEA45FB6263519B5002FA97D /* Build configuration list for PBXNativeTarget "iOS-SE" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/UTM.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/UTM.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 1088cbfa..a834a08b 100644 --- a/UTM.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/UTM.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -16,7 +16,7 @@ "location" : "https://github.com/utmapp/CocoaSpice.git", "state" : { "branch" : "main", - "revision" : "52b1535824657354fc3089eab24f1827280f9143" + "revision" : "9873c6cacf866c8f47964e777b53f5c8f47364d7" } }, { diff --git a/patches/data/qemu-10.0.2-utm/pc-bios/edk2-arm-secure-vars.fd.bz2 b/patches/data/qemu-10.0.2-utm/pc-bios/edk2-arm-secure-vars.fd.bz2 deleted file mode 100644 index db9447ff..00000000 Binary files a/patches/data/qemu-10.0.2-utm/pc-bios/edk2-arm-secure-vars.fd.bz2 and /dev/null differ diff --git a/patches/data/qemu-10.0.2-utm/pc-bios/edk2-arm-vars.fd.bz2 b/patches/data/qemu-10.0.2-utm/pc-bios/edk2-arm-vars.fd.bz2 deleted file mode 100644 index e072d875..00000000 Binary files a/patches/data/qemu-10.0.2-utm/pc-bios/edk2-arm-vars.fd.bz2 and /dev/null differ diff --git a/patches/data/qemu-10.0.2-utm/pc-bios/edk2-i386-secure-vars.fd.bz2 b/patches/data/qemu-10.0.2-utm/pc-bios/edk2-i386-secure-vars.fd.bz2 deleted file mode 100644 index 9dfabccd..00000000 Binary files a/patches/data/qemu-10.0.2-utm/pc-bios/edk2-i386-secure-vars.fd.bz2 and /dev/null differ diff --git a/patches/data/qemu-10.0.2-utm/pc-bios/edk2-i386-vars.fd.bz2 b/patches/data/qemu-10.0.2-utm/pc-bios/edk2-i386-vars.fd.bz2 deleted file mode 100644 index 5c764345..00000000 Binary files a/patches/data/qemu-10.0.2-utm/pc-bios/edk2-i386-vars.fd.bz2 and /dev/null differ diff --git a/patches/data/qemu-10.0.2-utm/pc-bios/m68k-declrom b/patches/data/qemu-10.0.2-utm/pc-bios/m68k-declrom deleted file mode 100755 index 1dffe0a8..00000000 Binary files a/patches/data/qemu-10.0.2-utm/pc-bios/m68k-declrom and /dev/null differ diff --git a/patches/data/qemu-10.0.2-utm/pc-bios/ppc-ndrvloader b/patches/data/qemu-10.0.2-utm/pc-bios/ppc-ndrvloader deleted file mode 100644 index 46de171d..00000000 Binary files a/patches/data/qemu-10.0.2-utm/pc-bios/ppc-ndrvloader and /dev/null differ diff --git a/patches/qemu-10.0.12-utm.patch b/patches/qemu-10.0.12-utm.patch new file mode 100644 index 00000000..2cc6a8f2 --- /dev/null +++ b/patches/qemu-10.0.12-utm.patch @@ -0,0 +1,548 @@ +From 263d9536fa0f652024b90103b1c9fffd57cc2542 Mon Sep 17 00:00:00 2001 +From: osy +Date: Tue, 28 Jul 2026 10:04:14 -0700 +Subject: [PATCH 1/4] spice-display: avoid sending duplicate scanout + +If the same surface + size is sent, ignore sending scanout to the client. + +Guests using flip-model presentation issue a set_scanout for every frame +with a different backing buffer of the same dimensions. The IOSurface and +its ID are unchanged in that case, so re-announcing it also closes the +previous pipe and trips the client's stale-POLLHUP protection. Frame +content is delivered by gl_draw regardless. + +Assisted-by: Claude:claude-fable-5 +--- + include/ui/spice-display.h | 4 ++++ + ui/spice-display.c | 42 ++++++++++++++++++++++++++++---------- + 2 files changed, 35 insertions(+), 11 deletions(-) + +diff --git a/include/ui/spice-display.h b/include/ui/spice-display.h +index 8e752f67da..cc052f0a08 100644 +--- a/include/ui/spice-display.h ++++ b/include/ui/spice-display.h +@@ -145,6 +145,10 @@ struct SimpleSpiceDisplay { + #if defined(CONFIG_IOSURFACE) + IOSurfaceRef iosurface; + int surface_send_fd; ++ /* last geometry sent via spice_qxl_gl_scanout/monitor_config so we can ++ * skip re-sending the scanout for pure back-buffer flips */ ++ uint32_t scanout_last_x, scanout_last_y, scanout_last_w, scanout_last_h; ++ bool scanout_last_y0top; + #if defined(CONFIG_METAL) + SpiceDisplayMetalContext metal_context; + #endif +diff --git a/ui/spice-display.c b/ui/spice-display.c +index 976f68710a..82d758ae08 100644 +--- a/ui/spice-display.c ++++ b/ui/spice-display.c +@@ -995,18 +995,19 @@ static void spice_iosurface_destroy(SimpleSpiceDisplay *ssd) + ssd->iosurface = NULL; + } + +-static bool spice_iosurface_resize(SimpleSpiceDisplay *ssd, int width, int height) ++/* returns -1 on error, 0 if the surface is unchanged, 1 if (re)created */ ++static int spice_iosurface_resize(SimpleSpiceDisplay *ssd, int width, int height) + { + if (ssd->iosurface) { + if (IOSurfaceGetHeight(ssd->iosurface) != height || + IOSurfaceGetWidth(ssd->iosurface) != width) { + spice_iosurface_destroy(ssd); +- return spice_iosurface_create(ssd, width, height); ++ return spice_iosurface_create(ssd, width, height) ? 1 : -1; + } else { +- return true; ++ return 0; + } + } else { +- return spice_iosurface_create(ssd, width, height); ++ return spice_iosurface_create(ssd, width, height) ? 1 : -1; + } + } + +@@ -1277,7 +1278,7 @@ static void spice_gl_switch(DisplayChangeListener *dcl, + qemu_spice_display_metal_scanout_disable(ssd->metal_context); + } + #endif +- if (spice_iosurface_resize(ssd, width, height)) { ++ if (spice_iosurface_resize(ssd, width, height) >= 0) { + fd = spice_iosurface_create_fd(ssd, &fourcc); + if (fd < 0) { + error_report("spice_gl_switch: failed to create fd"); +@@ -1390,13 +1391,12 @@ static void qemu_spice_gl_scanout_texture(DisplayChangeListener *dcl, + #if defined(CONFIG_GBM) + fd = egl_get_fd_for_texture(tex_id, &stride, &fourcc, NULL); + #elif defined(CONFIG_IOSURFACE) +- if (spice_iosurface_resize(ssd, backing_width, backing_height)) { +- ssd->tex_id = tex_id; +- ssd->y_0_top = y_0_top; +- fd = spice_iosurface_create_fd(ssd, &fourcc); +- } else { +- fd = -1; ++ int res = spice_iosurface_resize(ssd, backing_width, backing_height); ++ if (res < 0) { ++ fprintf(stderr, "%s: failed to create IOSurface\n", __func__); ++ return; + } ++ ssd->tex_id = tex_id; + #if defined(CONFIG_METAL) + if (ssd->metal_context && native.type == SCANOUT_TEXTURE_NATIVE_TYPE_METAL) { + qemu_spice_display_metal_scanout_texture(ssd->metal_context, +@@ -1406,6 +1406,26 @@ static void qemu_spice_gl_scanout_texture(DisplayChangeListener *dcl, + qemu_spice_display_metal_scanout_disable(ssd->metal_context); + } + #endif ++ /* ++ * Guests using flip-model presentation issue a set_scanout for every ++ * frame with a different backing buffer of the same dimensions. The ++ * IOSurface (and its ID) is unchanged in that case, so avoid spamming ++ * the SPICE client with gl_scanout/monitors_config messages (which ++ * would also close the previous pipe and trigger the client's ++ * stale-POLLHUP protection). Frame content is delivered by gl_draw. ++ */ ++ if (res == 0 && ssd->have_scanout && ++ ssd->y_0_top == y_0_top && ++ ssd->scanout_last_x == x && ssd->scanout_last_y == y && ++ ssd->scanout_last_w == w && ssd->scanout_last_h == h) { ++ return; ++ } ++ ssd->y_0_top = y_0_top; ++ ssd->scanout_last_x = x; ++ ssd->scanout_last_y = y; ++ ssd->scanout_last_w = w; ++ ssd->scanout_last_h = h; ++ fd = spice_iosurface_create_fd(ssd, &fourcc); + #endif + if (fd < 0) { + fprintf(stderr, "%s: failed to get fd for texture\n", __func__); +-- +2.41.0 + +From 7e67e276de77359eeccd7eeacdc644e735919280 Mon Sep 17 00:00:00 2001 +From: osy +Date: Tue, 28 Jul 2026 10:04:32 -0700 +Subject: [PATCH 2/4] hvf: fix lost interrupt delivery during WFI + +cpus_kick_thread() can race hvf_wait_for_ipi() and deadlock the VM: its +thread_kicked dedup can swallow a kick that was issued right after +hvf_wait_for_ipi() cleared the flag but before it entered pselect(). The +vCPU then sleeps in WFI with the interrupt or stop request still pending, +which stalls guest interrupts indefinitely and hangs pause_all_vcpus() on +guest reset. + +Always send the signal instead of relying on the dedup, and bound the +otherwise open-ended pselect() so that a kick lost for any other reason +degrades to a short delay rather than an unbounded sleep. + +Assisted-by: Claude:claude-fable-5 +--- + target/arm/hvf/hvf.c | 33 +++++++++++++++++++++++++++++++-- + 1 file changed, 31 insertions(+), 2 deletions(-) + +diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c +index 1f35d9f4d4..b20d70458f 100644 +--- a/target/arm/hvf/hvf.c ++++ b/target/arm/hvf/hvf.c +@@ -1242,7 +1242,21 @@ int hvf_arch_init_vcpu(CPUState *cpu) + + void hvf_kick_vcpu_thread(CPUState *cpu) + { +- cpus_kick_thread(cpu); ++ /* ++ * Do not use cpus_kick_thread(): its thread_kicked dedup races with ++ * hvf_wait_for_ipi() clearing thread_kicked right before an unbounded ++ * pselect(). A kick that is skipped because thread_kicked was still set ++ * from an already-consumed kick is lost forever: the vCPU sleeps in WFI ++ * with the interrupt (or stop request) pending, which delays guest ++ * interrupts indefinitely and deadlocks pause_all_vcpus() on guest ++ * reset (zombie VM). Always send the signal. ++ */ ++ qatomic_set(&cpu->thread_kicked, true); ++ int err = pthread_kill(cpu->thread->thread, SIG_IPI); ++ if (err && err != ESRCH) { ++ fprintf(stderr, "qemu:%s: %s", __func__, strerror(err)); ++ exit(1); ++ } + hv_vcpus_exit(&cpu->accel->fd, 1); + } + +@@ -1967,13 +1981,28 @@ static uint64_t hvf_vtimer_val(void) + + static void hvf_wait_for_ipi(CPUState *cpu, struct timespec *ts) + { ++ /* ++ * Cap open-ended waits so that a lost kick degrades to a short delay ++ * instead of an unbounded sleep (defense in depth on top of the ++ * unconditional signal in hvf_kick_vcpu_thread()). ++ */ ++ struct timespec bounded = { .tv_sec = 0, .tv_nsec = 10 * SCALE_MS }; ++ + /* + * Use pselect to sleep so that other threads can IPI us while we're + * sleeping. + */ + qatomic_set_mb(&cpu->thread_kicked, false); ++ /* ++ * A kick sent before thread_kicked was cleared may have been deduped; ++ * re-check the conditions it would have signalled before sleeping. ++ */ ++ if (qatomic_read(&cpu->stop) || ++ (cpu->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_FIQ))) { ++ return; ++ } + bql_unlock(); +- pselect(0, 0, 0, 0, ts, &cpu->accel->unblock_ipi_mask); ++ pselect(0, 0, 0, 0, ts ? ts : &bounded, &cpu->accel->unblock_ipi_mask); + bql_lock(); + } + +-- +2.41.0 + +From 6c0ee265362b5cd22786f38f21806465c064b502 Mon Sep 17 00:00:00 2001 +From: osy +Date: Wed, 29 Jul 2026 01:02:20 -0700 +Subject: [PATCH 3/4] ui/spice: carry the guest scanout's pixel format to the + IOSurface + +The shared IOSurface was created with a hardcoded 'BGRA' pixel format, +and that format was hardcoded again at every consumer, including the +fourcc reported to the client through spice_qxl_gl_scanout(). + +A scanout is in whatever format the guest allocated. That is normally +BGRA, but a fullscreen D3D swapchain becomes the scanout directly and may +be R8G8B8A8, so its bytes ended up in a surface every consumer read as +BGRA and red and blue came out transposed. Windowed output was unaffected +because the scanout is then the desktop primary, which is BGRA. + +Take the format from the scanout MTLTexture, recreate the surface when it +changes just as for a size change, and report the real fourcc. +SpiceGlScanout.format is documented as the drm fourcc format; it was +being filled with a constant. + +The EGL path takes the format too. The CGL path cannot: for 8-bit colour +CGLTexImageIOSurface2D only accepts GL_BGRA with +GL_UNSIGNED_INT_8_8_8_8_REV, and that combination reads the surface as +BGRA no matter what its fourcc says, so a non-BGRA surface there is +refused rather than silently transposed. Only the Metal path is ever +asked for a different order. + +Assisted-by: Claude:claude-opus-5 +--- + include/ui/spice-display.h | 5 +++ + ui/spice-display-metal.m | 23 +++++++++++++- + ui/spice-display.c | 65 +++++++++++++++++++++++++++++--------- + 3 files changed, 77 insertions(+), 16 deletions(-) + +diff --git a/include/ui/spice-display.h b/include/ui/spice-display.h +index cc052f0a08..965e248511 100644 +--- a/include/ui/spice-display.h ++++ b/include/ui/spice-display.h +@@ -144,6 +144,9 @@ struct SimpleSpiceDisplay { + #endif + #if defined(CONFIG_IOSURFACE) + IOSurfaceRef iosurface; ++ /* IOSurface pixel format ('BGRA'/'RGBA') the surface was created with; ++ * a guest scanout in a different channel order forces a recreate. */ ++ uint32_t iosurface_fourcc; + int surface_send_fd; + /* last geometry sent via spice_qxl_gl_scanout/monitor_config so we can + * skip re-sending the scanout for pure back-buffer flips */ +@@ -220,6 +223,8 @@ SpiceDisplayMetalContext qemu_spice_display_metal_create_context(IOSurfaceRef su + uint32_t width, + uint32_t height); + void qemu_spice_display_metal_destroy_context(SpiceDisplayMetalContext ctx); ++/* IOSurface pixel format matching a scanout MTLTexture, 0 if unsupported. */ ++uint32_t qemu_spice_display_metal_texture_fourcc(void *texture); + void qemu_spice_display_metal_scanout_texture(SpiceDisplayMetalContext ctx, + MTLTexture_id tex, uint32_t x, uint32_t y, + uint32_t w, uint32_t h); +diff --git a/ui/spice-display-metal.m b/ui/spice-display-metal.m +index 29e58d9461..23d8408068 100644 +--- a/ui/spice-display-metal.m ++++ b/ui/spice-display-metal.m +@@ -38,7 +38,10 @@ - (instancetype)initWithDevice:(id)device + { + if (self = [super init]) { + MTLTextureDescriptor *textureDescriptor = [[MTLTextureDescriptor alloc] init]; +- textureDescriptor.pixelFormat = MTLPixelFormatBGRA8Unorm; ++ /* must match the surface, which follows the guest scanout's order */ ++ textureDescriptor.pixelFormat = ++ IOSurfaceGetPixelFormat(surface) == 'RGBA' ? MTLPixelFormatRGBA8Unorm ++ : MTLPixelFormatBGRA8Unorm; + textureDescriptor.width = width; + textureDescriptor.height = height; + textureDescriptor.usage = MTLTextureUsageRenderTarget; +@@ -112,6 +115,24 @@ - (void)drawFrameAtRect:(CGRect)rect completion:(void (^)(void))completion + + @end + ++uint32_t qemu_spice_display_metal_texture_fourcc(void *texture) ++{ ++ id tex = (id)texture; ++ ++ if (!tex) { ++ return 0; ++ } ++ switch (tex.pixelFormat) { ++ case MTLPixelFormatBGRA8Unorm: ++ return 'BGRA'; ++ case MTLPixelFormatRGBA8Unorm: ++ return 'RGBA'; ++ default: ++ /* no IOSurface equivalent wired up; caller keeps its default */ ++ return 0; ++ } ++} ++ + SpiceDisplayMetalContext qemu_spice_display_metal_create_context(IOSurfaceRef surface, + uint32_t width, + uint32_t height) +diff --git a/ui/spice-display.c b/ui/spice-display.c +index 82d758ae08..d73fbf5271 100644 +--- a/ui/spice-display.c ++++ b/ui/spice-display.c +@@ -818,9 +818,10 @@ static void AddIntegerValue(CFMutableDictionaryRef dictionary, const CFStringRef + CFRelease(number); + } + +-static bool spice_iosurface_create_egl(SimpleSpiceDisplay *ssd, int width, int height, +- IOSurfaceRef surface) ++static bool spice_iosurface_create_egl_fmt(SimpleSpiceDisplay *ssd, int width, int height, ++ IOSurfaceRef surface, uint32_t fourcc) + { ++ const GLenum egl_internal_format = (fourcc == 'RGBA') ? GL_RGBA : GL_BGRA_EXT; + #if defined(CONFIG_EGL) + EGLint target = 0; + GLenum tex_target = 0; +@@ -845,7 +846,7 @@ static bool spice_iosurface_create_egl(SimpleSpiceDisplay *ssd, int width, int h + EGL_HEIGHT, height, + EGL_IOSURFACE_PLANE_ANGLE, 0, + EGL_TEXTURE_TARGET, target, +- EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_BGRA_EXT, ++ EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, egl_internal_format, + EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA, + EGL_TEXTURE_TYPE_ANGLE, GL_UNSIGNED_BYTE, + EGL_IOSURFACE_USAGE_HINT_ANGLE, EGL_IOSURFACE_WRITE_HINT_ANGLE, +@@ -872,11 +873,23 @@ static bool spice_iosurface_create_egl(SimpleSpiceDisplay *ssd, int width, int h + } + + static bool spice_iosurface_create_cgl(SimpleSpiceDisplay *ssd, int width, int height, +- IOSurfaceRef surface) ++ IOSurfaceRef surface, uint32_t fourcc) + { + #if defined(HAVE_SPICE_MAC_CGL) + GLuint tex; + ++ /* ++ * CGLTexImageIOSurface2D accepts only GL_BGRA/GL_UNSIGNED_INT_8_8_8_8_REV ++ * for 8-bit colour (see the table in ; anything else ++ * returns kCGLBadValue), and that combination reads the surface as BGRA ++ * whatever its fourcc says. So this path cannot carry a differently-ordered ++ * scanout at all, and must refuse rather than silently transpose. ++ */ ++ if (fourcc != 'BGRA') { ++ error_report("spice_iosurface_create: CGL supports BGRA surfaces only"); ++ return false; ++ } ++ + CGLSetCurrentContext(spice_gl_ctx); + glGenTextures(1, &tex); + glBindTexture(GL_TEXTURE_RECTANGLE, tex); +@@ -904,14 +917,15 @@ static bool spice_iosurface_create_cgl(SimpleSpiceDisplay *ssd, int width, int h + #endif + } + +-static bool spice_iosurface_create(SimpleSpiceDisplay *ssd, int width, int height) ++static bool spice_iosurface_create(SimpleSpiceDisplay *ssd, int width, int height, ++ uint32_t fourcc) + { + IOSurfaceRef surface; + CFMutableDictionaryRef dict = CFDictionaryCreateMutable( + kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + AddIntegerValue(dict, kIOSurfaceWidth, width); + AddIntegerValue(dict, kIOSurfaceHeight, height); +- AddIntegerValue(dict, kIOSurfacePixelFormat, 'BGRA'); ++ AddIntegerValue(dict, kIOSurfacePixelFormat, (int)fourcc); + AddIntegerValue(dict, kIOSurfaceBytesPerElement, 4); + #if TARGET_OS_OSX + CFDictionaryAddValue(dict, kIOSurfaceIsGlobal, kCFBooleanTrue); +@@ -926,18 +940,19 @@ static bool spice_iosurface_create(SimpleSpiceDisplay *ssd, int width, int heigh + } + + if (spice_opengl == DISPLAY_GL_MODE_CORE) { +- if (!spice_iosurface_create_cgl(ssd, width, height, surface)) { ++ if (!spice_iosurface_create_cgl(ssd, width, height, surface, fourcc)) { + CFRelease(surface); + return false; + } + } else { +- if (!spice_iosurface_create_egl(ssd, width, height, surface)) { ++ if (!spice_iosurface_create_egl_fmt(ssd, width, height, surface, fourcc)) { + CFRelease(surface); + return false; + } + } + + ssd->iosurface = surface; ++ ssd->iosurface_fourcc = fourcc; + + #if defined(CONFIG_METAL) + ssd->metal_context = qemu_spice_display_metal_create_context(surface, width, height); +@@ -996,18 +1011,20 @@ static void spice_iosurface_destroy(SimpleSpiceDisplay *ssd) + } + + /* returns -1 on error, 0 if the surface is unchanged, 1 if (re)created */ +-static int spice_iosurface_resize(SimpleSpiceDisplay *ssd, int width, int height) ++static int spice_iosurface_resize(SimpleSpiceDisplay *ssd, int width, int height, ++ uint32_t fourcc) + { + if (ssd->iosurface) { + if (IOSurfaceGetHeight(ssd->iosurface) != height || +- IOSurfaceGetWidth(ssd->iosurface) != width) { ++ IOSurfaceGetWidth(ssd->iosurface) != width || ++ ssd->iosurface_fourcc != fourcc) { + spice_iosurface_destroy(ssd); +- return spice_iosurface_create(ssd, width, height) ? 1 : -1; ++ return spice_iosurface_create(ssd, width, height, fourcc) ? 1 : -1; + } else { + return 0; + } + } else { +- return spice_iosurface_create(ssd, width, height) ? 1 : -1; ++ return spice_iosurface_create(ssd, width, height, fourcc) ? 1 : -1; + } + } + +@@ -1031,7 +1048,7 @@ static int spice_iosurface_create_fd(SimpleSpiceDisplay *ssd, int *fourcc) + // when we close it, POLLHUP will be seen by the other side and know that + // the surface ID is stale and should not be used + ssd->surface_send_fd = fds[1]; +- *fourcc = 'BGRA'; ++ *fourcc = (int)ssd->iosurface_fourcc; + surfaceid = IOSurfaceGetID(ssd->iosurface); + write(ssd->surface_send_fd, &surfaceid, sizeof(surfaceid)); + return fds[0]; +@@ -1278,7 +1295,7 @@ static void spice_gl_switch(DisplayChangeListener *dcl, + qemu_spice_display_metal_scanout_disable(ssd->metal_context); + } + #endif +- if (spice_iosurface_resize(ssd, width, height) >= 0) { ++ if (spice_iosurface_resize(ssd, width, height, 'BGRA') >= 0) { + fd = spice_iosurface_create_fd(ssd, &fourcc); + if (fd < 0) { + error_report("spice_gl_switch: failed to create fd"); +@@ -1391,7 +1408,25 @@ static void qemu_spice_gl_scanout_texture(DisplayChangeListener *dcl, + #if defined(CONFIG_GBM) + fd = egl_get_fd_for_texture(tex_id, &stride, &fourcc, NULL); + #elif defined(CONFIG_IOSURFACE) +- int res = spice_iosurface_resize(ssd, backing_width, backing_height); ++ /* ++ * The surface is shared with the client verbatim, so it has to be in the ++ * guest scanout's channel order: a fullscreen D3D swapchain can be ++ * R8G8B8A8 while the desktop is B8G8R8A8. Only the Metal path can honour ++ * a non-BGRA order end to end, so everything else stays BGRA. ++ */ ++ uint32_t want_fourcc = 'BGRA'; ++#if defined(CONFIG_METAL) ++ if (native.type == SCANOUT_TEXTURE_NATIVE_TYPE_METAL && ++ spice_opengl != DISPLAY_GL_MODE_CORE) { ++ uint32_t native_fourcc = ++ qemu_spice_display_metal_texture_fourcc(native.handle); ++ if (native_fourcc) { ++ want_fourcc = native_fourcc; ++ } ++ } ++#endif ++ int res = spice_iosurface_resize(ssd, backing_width, backing_height, ++ want_fourcc); + if (res < 0) { + fprintf(stderr, "%s: failed to create IOSurface\n", __func__); + return; +-- +2.41.0 + +From 27d8515de35937969dec6144dc6a0b63362d5ecc Mon Sep 17 00:00:00 2001 +From: osy +Date: Wed, 29 Jul 2026 16:20:12 -0700 +Subject: [PATCH 4/4] Revert "ui/console: force gl-unblock from the watchdog + timer" + +This reverts commit 5cb1434ba33797b08f94c0f2168cd5ee7f70d8e6. +--- + ui/console.c | 45 ++------------------------------------------- + 1 file changed, 2 insertions(+), 43 deletions(-) + +diff --git a/ui/console.c b/ui/console.c +index a2dddbd37a..f6d7879eff 100644 +--- a/ui/console.c ++++ b/ui/console.c +@@ -166,32 +166,7 @@ void qemu_console_co_wait_update(QemuConsole *con) + + static void graphic_hw_gl_unblock_timer(void *opaque) + { +- QemuConsole *con = opaque; +- +- /* +- * The display's GL render did not release the block within the timeout +- * (e.g. the gtk window is occluded, the host compositor throttled the +- * frame callback, or the host GPU stalled). The device's command queue is +- * gated on this block (virtio-gpu: renderer_blocked), so a stuck display +- * stalls the guest GPU scheduler into a TDR (dxgmms1 0x7E) ~2 s later. +- * Force the unblock so guest command/fence processing resumes well before +- * the guest's TdrDelay; the worst case is one stale/torn displayed frame, +- * never a guest crash. A late real release is absorbed in +- * graphic_hw_gl_block() (con->gl_block already 0 -> no-op). +- */ +- /* Rate-limited: this can fire repeatedly when the display fence is +- * persistently slow; an occasional line is enough to know it's active. */ +- static unsigned _ub; +- if ((_ub++ % 60u) == 0u) { +- warn_report("console: gl-unblock fallback firing (display GL fence slow); " +- "forcing unblock to keep the guest cmdq moving"); +- } +- if (con->gl_block > 0) { +- con->gl_block = 0; +- if (con->hw_ops->gl_block) { +- con->hw_ops->gl_block(con->hw, false); +- } +- } ++ warn_report("console: no gl-unblock within one second"); + } + + void graphic_hw_gl_block(QemuConsole *con, bool block) +@@ -202,13 +177,6 @@ void graphic_hw_gl_block(QemuConsole *con, bool block) + if (block) { + con->gl_block++; + } else { +- /* +- * A late release after graphic_hw_gl_unblock_timer() force-cleared the +- * block (con->gl_block already 0) must be a safe no-op, not underflow. +- */ +- if (con->gl_block == 0) { +- return; +- } + con->gl_block--; + } + assert(con->gl_block >= 0); +@@ -222,16 +190,7 @@ void graphic_hw_gl_block(QemuConsole *con, bool block) + + if (block) { + timeout = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); +- /* Safety net only. The display samples (blits + fences) the guest +- * buffer directly at flush time, so the block lifetime is the GPU +- * fence latency -- not the host compositor's frame clock -- and +- * releasing the guest early TEARS: it re-renders into the buffer +- * while the queued blit still reads it (scaled/shifted frame +- * artifacts under present storms). 500ms is correct backpressure +- * for a slow (shared, possibly saturated) GPU while still +- * recovering a genuinely stuck fence well inside the guest's ~2s +- * TDR budget. */ +- timeout += 500; ++ timeout += 1000; /* one sec */ + timer_mod(con->gl_unblock_timer, timeout); + } else { + timer_del(con->gl_unblock_timer); +-- +2.41.0 + diff --git a/patches/qemu-10.0.2-utm.patch b/patches/qemu-10.0.2-utm.patch deleted file mode 100644 index 55e99028..00000000 --- a/patches/qemu-10.0.2-utm.patch +++ /dev/null @@ -1,4974 +0,0 @@ -From 594fe2f680f571dfe1d69281b803bdcbac925f7a Mon Sep 17 00:00:00 2001 -From: osy -Date: Fri, 18 Jul 2025 13:29:23 -0700 -Subject: [PATCH 1/2] tcg/tcti: implement vector immediate shifts - -This now seems to be required as a result of the introduction of -gen_gvec_rev{16,32,64} in 38f9950c8e0315d7b26803018a3f73d5f42e6703. ---- - tcg/aarch64-tcti/tcg-target-has.h | 2 +- - tcg/aarch64-tcti/tcg-target-opc.h.inc | 1 + - tcg/aarch64-tcti/tcg-target.c.inc | 45 +++++++++++++++++-- - tcg/aarch64-tcti/tcti-gadget-gen.py | 63 +++++++++++++++++++++++++-- - 4 files changed, 103 insertions(+), 8 deletions(-) - -diff --git a/tcg/aarch64-tcti/tcg-target-has.h b/tcg/aarch64-tcti/tcg-target-has.h -index 8e39891c02..67b50fcdea 100644 ---- a/tcg/aarch64-tcti/tcg-target-has.h -+++ b/tcg/aarch64-tcti/tcg-target-has.h -@@ -84,7 +84,7 @@ - #define TCG_TARGET_HAS_roti_vec 0 - #define TCG_TARGET_HAS_rots_vec 0 - #define TCG_TARGET_HAS_rotv_vec 0 --#define TCG_TARGET_HAS_shi_vec 0 -+#define TCG_TARGET_HAS_shi_vec 1 - #define TCG_TARGET_HAS_shs_vec 0 - #define TCG_TARGET_HAS_shv_vec 1 - #define TCG_TARGET_HAS_mul_vec 1 -diff --git a/tcg/aarch64-tcti/tcg-target-opc.h.inc b/tcg/aarch64-tcti/tcg-target-opc.h.inc -index 88cf2bc53d..5382315c41 100644 ---- a/tcg/aarch64-tcti/tcg-target-opc.h.inc -+++ b/tcg/aarch64-tcti/tcg-target-opc.h.inc -@@ -12,3 +12,4 @@ - */ - - DEF(aa64_sshl_vec, 1, 2, 0, TCG_OPF_VECTOR) -+DEF(aa64_sli_vec, 1, 2, 1, TCG_OPF_VECTOR) -diff --git a/tcg/aarch64-tcti/tcg-target.c.inc b/tcg/aarch64-tcti/tcg-target.c.inc -index 7b1186cd01..8b78abe4bb 100644 ---- a/tcg/aarch64-tcti/tcg-target.c.inc -+++ b/tcg/aarch64-tcti/tcg-target.c.inc -@@ -217,6 +217,8 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) - return C_O1_I2(w, w, w); - case INDEX_op_bitsel_vec: - return C_O1_I3(w, w, w, w); -+ case INDEX_op_aa64_sli_vec: -+ return C_O1_I2(w, 0, w); - - default: - return C_NotImplemented; -@@ -490,6 +492,13 @@ static void tcg_out_ternary_gadget(TCGContext *s, const void *gadget_base[TCG_TA - tcg_out_gadget(s, gadget_base[reg0][reg1][reg2]); - } - -+ -+/* Write gadget pointer (three registers, last is immediate value). */ -+static void tcg_out_ternary_immediate_gadget(TCGContext *s, const void *gadget_base[TCG_TARGET_GP_REGS][TCG_TARGET_GP_REGS][TCTI_GADGET_IMMEDIATE_ARRAY_LEN], unsigned reg0, unsigned reg1, unsigned reg2) -+{ -+ tcg_out_gadget(s, gadget_base[reg0][reg1][reg2]); -+} -+ - /*************************** - * TCG Scalar Operations * - ***************************/ -@@ -1558,13 +1567,18 @@ static void tcg_out_nop_fill(tcg_insn_unit *p, int count) - tcg_out_sized_vector_gadget_no64(s, name, ternary, vece, a, b, c) - - --#define tcg_out_ternary_vector_gadget_with_scalar(s, name, is_scalar, vece, a, b, c) \ -+#define tcg_out_sized_gadget_with_scalar(s, name, arity, is_scalar, vece, args...) \ - if (is_scalar) { \ -- tcg_out_ternary_gadget(s, gadget_ ## name ## _scalar, w0, w1, w2); \ -+ tcg_out_ ## arity ## _gadget(s, gadget_ ## name ## _scalar, args); \ - } else { \ -- tcg_out_ternary_vector_gadget(s, name, vece, w0, w1, w2); \ -+ tcg_out_sized_vector_gadget(s, name, arity, vece, args); \ - } - -+#define tcg_out_ternary_vector_gadget_with_scalar(s, name, is_scalar, vece, a, b, c) \ -+ tcg_out_sized_gadget_with_scalar(s, name, ternary, is_scalar, vece, a, b, c) -+ -+#define tcg_out_ternary_immediate_vector_gadget_with_scalar(s, name, is_scalar, vece, a, b, c) \ -+ tcg_out_sized_gadget_with_scalar(s, name, ternary_immediate, is_scalar, vece, a, b, c) - - /* Return true if v16 is a valid 16-bit shifted immediate. */ - static bool is_shimm16(uint16_t v16, int *cmode, int *imm8) -@@ -1765,6 +1779,20 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, unsigned vecl, unsigned - break; - } - -+ /* inhibit compiler warning because we use imm as a register */ -+ case INDEX_op_shli_vec: -+ tcg_out_ternary_immediate_vector_gadget_with_scalar(s, shl, is_scalar, vece, w0, w1, r2); -+ break; -+ case INDEX_op_shri_vec: -+ tcg_out_ternary_immediate_vector_gadget_with_scalar(s, ushr, is_scalar, vece, w0, w1, r2 - 1); -+ break; -+ case INDEX_op_sari_vec: -+ tcg_out_ternary_immediate_vector_gadget_with_scalar(s, sshr, is_scalar, vece, w0, w1, r2 - 1); -+ break; -+ case INDEX_op_aa64_sli_vec: -+ tcg_out_ternary_immediate_vector_gadget_with_scalar(s, sli, is_scalar, vece, w0, w2, r3); -+ break; -+ - case INDEX_op_mov_vec: /* Always emitted via tcg_out_mov. */ - case INDEX_op_dup_vec: /* Always emitted via tcg_out_dup_vec. */ - default: -@@ -1787,6 +1815,9 @@ int tcg_can_emit_vec_op(TCGOpcode opc, TCGType type, unsigned vece) - case INDEX_op_abs_vec: - case INDEX_op_not_vec: - case INDEX_op_cmp_vec: -+ case INDEX_op_shli_vec: -+ case INDEX_op_shri_vec: -+ case INDEX_op_sari_vec: - case INDEX_op_ssadd_vec: - case INDEX_op_sssub_vec: - case INDEX_op_usadd_vec: -@@ -1827,6 +1858,14 @@ void tcg_expand_vec_op(TCGOpcode opc, TCGType type, unsigned vece, - va_end(va); - - switch (opc) { -+ case INDEX_op_rotli_vec: -+ t1 = tcg_temp_new_vec(type); -+ tcg_gen_shri_vec(vece, t1, v1, -a2 & ((8 << vece) - 1)); -+ vec_gen_4(INDEX_op_aa64_sli_vec, type, vece, -+ tcgv_vec_arg(v0), tcgv_vec_arg(t1), tcgv_vec_arg(v1), a2); -+ tcg_temp_free_vec(t1); -+ break; -+ - case INDEX_op_shrv_vec: - case INDEX_op_sarv_vec: - /* Right shifts are negative left shifts for AArch64. */ -diff --git a/tcg/aarch64-tcti/tcti-gadget-gen.py b/tcg/aarch64-tcti/tcti-gadget-gen.py -index 275c4ba943..ebed824500 100755 ---- a/tcg/aarch64-tcti/tcti-gadget-gen.py -+++ b/tcg/aarch64-tcti/tcti-gadget-gen.py -@@ -113,7 +113,7 @@ def simple(name, *lines, export=True): - - - --def with_register_substitutions(name, substitutions, *lines, immediate_range=range(0)): -+def with_register_substitutions(name, substitutions, *lines, immediate_range=range(0), filter=lambda p: False): - """ Generates a collection of gadgtes with register substitutions. """ - - def _expand_op1_immediate(num): -@@ -166,6 +166,10 @@ def substitutions_for_letter(letter, number, line): - - # For each permutation... - for permutation in permutations: -+ # Filter any invalid combination -+ if filter(permutation): -+ continue -+ - new_lines = lines - - # Replace each placeholder element with its proper value... -@@ -212,9 +216,9 @@ def with_dnm(name, *lines): - print("};", file=c_file) - - --def with_dn_immediate(name, *lines, immediate_range): -+def with_dn_immediate(name, *lines, immediate_range, filter=lambda m: False): - """ Generates a collection of gadgets with substitutions for Xd, Xn, and Xm, and equivalents. """ -- with_register_substitutions(name, ["d", "n"], *lines, immediate_range=immediate_range) -+ with_register_substitutions(name, ["d", "n"], *lines, immediate_range=immediate_range, filter=lambda p: filter(p[-1])) - - # Fetch the files we'll be using for output. - c_file, h_file = _get_output_files() -@@ -236,7 +240,10 @@ def with_dn_immediate(name, *lines, immediate_range): - - # M array - for i in immediate_range: -- print(f"gadget_{name}_arg{d}_arg{n}_arg{i}", end=", ", file=c_file) -+ if filter(i): -+ print(f"(void *)0", end=", ", file=c_file) -+ else: -+ print(f"gadget_{name}_arg{d}_arg{n}_arg{i}", end=", ", file=c_file) - - print("},", file=c_file) - print("\t},", file=c_file) -@@ -625,6 +632,24 @@ def do_size_replacement(line, size): - sized_lines = (scalar,) - with_dnm(f"{name}_scalar", *sized_lines) - -+def vector_dn_immediate(name, *lines, scalar=None, immediate_range, omit_sizes=(), filter=lambda s, m: False): -+ """ Creates a set of gadgets for every size of a given vector op. Accepts 'S' as a size placeholder. """ -+ -+ def do_size_replacement(line, size): -+ return line.replace(".S", f".{size}") -+ -+ # Create a variant for each size, replacing any placeholders. -+ for size in VECTOR_SIZES: -+ if size in omit_sizes: -+ continue -+ -+ sized_lines = (do_size_replacement(line, size) for line in lines) -+ with_dn_immediate(f"{name}_{size}", *sized_lines, immediate_range=immediate_range, filter=lambda m: filter(size, m)) -+ -+ if scalar: -+ if isinstance(scalar, str): -+ sized_lines = (scalar,) -+ with_dn_immediate(f"{name}_scalar", *sized_lines, immediate_range=immediate_range, filter=lambda m: filter(None, m)) - - def vector_math_dnm(name, operation): - """ Generates a collection of gadgets for vector math instructions. """ -@@ -647,6 +672,9 @@ def vector_logic_dnm(name, operation): - with_dnm(f"{name}_d", f"{operation} Vd.8b, Vn.8b, Vm.8b") - with_dnm(f"{name}_q", f"{operation} Vd.16b, Vn.16b, Vm.16b") - -+def vector_math_dn_immediate(name, operation, immediate_range, filter=lambda x: False): -+ """ Generates a collection of gadgets for vector math instructions. """ -+ vector_dn_immediate(name, f"{operation} Vd.S, Vn.S, #Ii", scalar=f"{operation} Dd, Dn, #Ii", immediate_range=immediate_range, filter=filter) - - # - # Gadget definitions. -@@ -1088,6 +1116,33 @@ def vector_logic_dnm(name, operation): - vector_math_dnm("shlv", "ushl") - vector_math_dnm("sshl", "sshl") - -+def filter_shl(size, imm): -+ match size: -+ case '16b': return imm >= 8 -+ case '8b': return imm >= 8 -+ case '4h': return imm >= 16 -+ case '8h': return imm >= 16 -+ case '2s': return imm >= 32 -+ case '4s': return imm >= 32 -+ return False -+ -+def filter_shr(size, imm): -+ if imm == 0: -+ return True -+ match size: -+ case '16b': return imm > 8 -+ case '8b': return imm > 8 -+ case '4h': return imm > 16 -+ case '8h': return imm > 16 -+ case '2s': return imm > 32 -+ case '4s': return imm > 32 -+ return False -+ -+vector_math_dn_immediate("shl", "shl", immediate_range=range(64), filter=filter_shl) -+vector_math_dn_immediate("ushr", "ushr", immediate_range=range(1,65), filter=filter_shr) -+vector_math_dn_immediate("sshr", "sshr", immediate_range=range(1,65), filter=filter_shr) -+vector_math_dn_immediate("sli", "sli", immediate_range=range(64), filter=filter_shl) -+ - vector_dnm("cmeq", "cmeq Vd.S, Vn.S, Vm.S", scalar="cmeq Dd, Dn, Dm") - vector_dnm("cmgt", "cmgt Vd.S, Vn.S, Vm.S", scalar="cmgt Dd, Dn, Dm") - vector_dnm("cmge", "cmge Vd.S, Vn.S, Vm.S", scalar="cmge Dd, Dn, Dm") --- -2.41.0 - -From 35ba52817dbea01821b429737e989bc54a14d411 Mon Sep 17 00:00:00 2001 -From: osy -Date: Tue, 22 Jul 2025 10:13:34 -0700 -Subject: [PATCH 2/2] tcg/tcti: fix goto as first instruction - ---- - accel/tcg/tcg-accel-ops.c | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/accel/tcg/tcg-accel-ops.c b/accel/tcg/tcg-accel-ops.c -index 0e8c4c1c67..e07f60357f 100644 ---- a/accel/tcg/tcg-accel-ops.c -+++ b/accel/tcg/tcg-accel-ops.c -@@ -68,6 +68,7 @@ void tcg_cpu_init_cflags(CPUState *cpu, bool parallel) - // GOTO_PTR is too complex to emit a simple gadget for. - // We'll let C handle it, since the overhead is similar. - cflags |= CF_NO_GOTO_PTR; -+ cpu->cflags_next_tb = CF_NO_GOTO_PTR; - #endif - tcg_cflags_set(cpu, cflags); - } --- -2.41.0 - -From 344a5a3cbe3df0c373743969493afe7d1c4fb4d6 Mon Sep 17 00:00:00 2001 -From: osy -Date: Sat, 2 Aug 2025 19:22:04 -0700 -Subject: [PATCH] pc-bios: add classicvirtio drivers for m68k/ppc - ---- - pc-bios/m68k-declrom | Bin 0 -> 106496 bytes - pc-bios/meson.build | 2 ++ - pc-bios/ppc-ndrvloader | Bin 0 -> 191172 bytes - 3 files changed, 2 insertions(+) - create mode 100755 pc-bios/m68k-declrom - create mode 100644 pc-bios/ppc-ndrvloader - -diff --git a/pc-bios/meson.build b/pc-bios/meson.build -index 9fb9659c45..63e10cc6df 100644 ---- a/pc-bios/meson.build -+++ b/pc-bios/meson.build -@@ -85,6 +85,8 @@ blobs = [ - 'npcm8xx_bootrom.bin', - 'vof.bin', - 'vof-nvram.bin', -+ 'm68k-declrom', -+ 'ppc-ndrvloader', - ] - - dtc = find_program('dtc', required: false) - -From a172998c2f8bcbd29afeb8cab9b97e43ef3a22b5 Mon Sep 17 00:00:00 2001 -From: osy -Date: Sun, 10 Aug 2025 21:54:34 -0700 -Subject: [PATCH] pc-bios: use 2023 Microsoft UEFI certificates - -Restore non-secure vars variants as well. ---- - pc-bios/edk2-arm-secure-vars.fd.bz2 | Bin 0 -> 12654 bytes - pc-bios/edk2-arm-vars.fd.bz2 | Bin 6710 -> 595 bytes - pc-bios/edk2-i386-secure-vars.fd.bz2 | Bin 0 -> 12986 bytes - pc-bios/edk2-i386-vars.fd.bz2 | Bin 7727 -> 612 bytes - pc-bios/meson.build | 2 ++ - 5 files changed, 2 insertions(+) - create mode 100644 pc-bios/edk2-arm-secure-vars.fd.bz2 - create mode 100644 pc-bios/edk2-i386-secure-vars.fd.bz2 - -diff --git a/pc-bios/meson.build b/pc-bios/meson.build -index 63e10cc6df..1e1b553795 100644 ---- a/pc-bios/meson.build -+++ b/pc-bios/meson.build -@@ -4,11 +4,13 @@ if unpack_edk2_blobs - 'edk2-aarch64-code.fd', - 'edk2-aarch64-secure-code.fd', - 'edk2-arm-code.fd', -+ 'edk2-arm-secure-vars.fd', - 'edk2-arm-vars.fd', - 'edk2-riscv-code.fd', - 'edk2-riscv-vars.fd', - 'edk2-i386-code.fd', - 'edk2-i386-secure-code.fd', -+ 'edk2-i386-secure-vars.fd', - 'edk2-i386-vars.fd', - 'edk2-x86_64-code.fd', - 'edk2-x86_64-secure-code.fd', --- -2.41.0 - -From 0f1d6606c28d0ae81a1b311972c5c54e5e867bf0 Mon Sep 17 00:00:00 2001 -From: Mark Cave-Ayland -Date: Wed, 11 Jun 2025 14:03:15 +0100 -Subject: [PATCH] target/i386: fix TB exit logic in gen_movl_seg() when writing - to SS - -Before commit e54ef98c8a ("target/i386: do not trigger IRQ shadow for LSS"), any -write to SS in gen_movl_seg() would cause a TB exit. The changes introduced by -this commit were intended to restrict the DISAS_EOB_INHIBIT_IRQ exit to the case -where inhibit_irq is true, but missed that a DISAS_EOB_NEXT exit can still be -required when writing to SS and inhibit_irq is false. - -Comparing the PE(s) && !VM86(s) section with the logic in x86_update_hflags(), we -can see that the DISAS_EOB_NEXT exit is still required for the !CODE32 case when -writing to SS in gen_movl_seg() because any change to the SS flags can affect -hflags. Similarly we can see that the existing CODE32 case is still correct since -a change to any of DS, ES and SS can affect hflags. Finally for the -gen_op_movl_seg_real() case an explicit TB exit is not needed because the segment -register selector does not affect hflags. - -Update the logic in gen_movl_seg() so that a write to SS with inhibit_irq set to -false where PE(s) && !VM86(s) will generate a DISAS_EOB_NEXT exit along with the -inline comment. This has the effect of allowing Win98SE to boot in QEMU once -again. - -Signed-off-by: Mark Cave-Ayland -Fixes: e54ef98c8a ("target/i386: do not trigger IRQ shadow for LSS") -Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2987 -Link: https://lore.kernel.org/r/20250611130315.383151-1-mark.cave-ayland@ilande.co.uk -Reviewed-by: Peter Maydell -Signed-off-by: Paolo Bonzini ---- - target/i386/tcg/translate.c | 7 +++++-- - 1 file changed, 5 insertions(+), 2 deletions(-) - -diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c -index 0fcddc2ec0..0cb87d0201 100644 ---- a/target/i386/tcg/translate.c -+++ b/target/i386/tcg/translate.c -@@ -2033,8 +2033,11 @@ static void gen_movl_seg(DisasContext *s, X86Seg seg_reg, TCGv src, bool inhibit - tcg_gen_trunc_tl_i32(sel, src); - gen_helper_load_seg(tcg_env, tcg_constant_i32(seg_reg), sel); - -- /* For move to DS/ES/SS, the addseg or ss32 flags may change. */ -- if (CODE32(s) && seg_reg < R_FS) { -+ /* -+ * For moves to SS, the SS32 flag may change. For CODE32 only, changes -+ * to SS, DS and ES may change the ADDSEG flags. -+ */ -+ if (seg_reg == R_SS || (CODE32(s) && seg_reg < R_FS)) { - s->base.is_jmp = DISAS_EOB_NEXT; - } - } else { --- -2.41.0 - -From a3955f90f898cfa8efcdeeab285324dcbb033b31 Mon Sep 17 00:00:00 2001 -From: osy -Date: Mon, 11 Aug 2025 22:24:52 -0700 -Subject: [PATCH] hw/i386/pc: no floppy when defaults disabled - ---- - hw/i386/pc_piix.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c -index 6c91e2d292..86978f4671 100644 ---- a/hw/i386/pc_piix.c -+++ b/hw/i386/pc_piix.c -@@ -465,7 +465,7 @@ static void pc_i440fx_machine_options(MachineClass *m) - m->default_machine_opts = "firmware=bios-256k.bin"; - m->default_display = "std"; - m->default_nic = "e1000"; -- m->no_floppy = !module_object_class_by_name(TYPE_ISA_FDC); -+ m->no_floppy = !defaults_enabled() || !module_object_class_by_name(TYPE_ISA_FDC); - m->no_parallel = !module_object_class_by_name(TYPE_ISA_PARALLEL); - machine_class_allow_dynamic_sysbus_dev(m, TYPE_RAMFB_DEVICE); - machine_class_allow_dynamic_sysbus_dev(m, TYPE_VMBUS_BRIDGE); -@@ -811,7 +811,7 @@ static void isapc_machine_options(MachineClass *m) - pcmc->has_reserved_memory = false; - m->default_nic = "ne2k_isa"; - m->default_cpu_type = X86_CPU_TYPE_NAME("486"); -- m->no_floppy = !module_object_class_by_name(TYPE_ISA_FDC); -+ m->no_floppy = !defaults_enabled() || !module_object_class_by_name(TYPE_ISA_FDC); - m->no_parallel = !module_object_class_by_name(TYPE_ISA_PARALLEL); - } - --- -2.41.0 - -From 60fb87f41c44a4fd76d6e4538d1746a6c3be162c Mon Sep 17 00:00:00 2001 -From: osy -Date: Sun, 14 Sep 2025 00:55:51 -0700 -Subject: [PATCH] tcg: new JIT workaround for iOS 26 - -We map the JIT region originally as RX and also mirror map -it as RX. Then we use attached debugger to flag the mirror -mapping as debugger owned. Finally, we change the original -map to RW which should not invalidate code-signing as the -mirror is debugger owned. - -Thanks to @JJTech0130 for this workaround. ---- - tcg/region.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++-- - 1 file changed, 62 insertions(+), 2 deletions(-) - -diff --git a/tcg/region.c b/tcg/region.c -index 70996b5ab1..a36c692bc3 100644 ---- a/tcg/region.c -+++ b/tcg/region.c -@@ -623,14 +623,57 @@ extern kern_return_t mach_vm_remap(vm_map_t target_task, - vm_prot_t *max_protection, - vm_inherit_t inheritance); - -+#include -+#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR -+#include -+#include -+static int is_debugger_attached(void) -+{ -+ int mib[4]; -+ struct kinfo_proc info; -+ size_t size; -+ -+ info.kp_proc.p_flag = 0; -+ -+ /* Initialize MIB for sysctl call */ -+ mib[0] = CTL_KERN; -+ mib[1] = KERN_PROC; -+ mib[2] = KERN_PROC_PID; -+ mib[3] = getpid(); -+ -+ size = sizeof(info); -+ -+ if (sysctl(mib, 4, &info, &size, NULL, 0) == -1) { -+ return 0; // sysctl failed, be conservative -+ } -+ -+ /* P_TRACED means the process is being debugged */ -+ return (info.kp_proc.p_flag & P_TRACED) != 0; -+} -+ -+static void break_prepare_jit_region(mach_vm_address_t addr, size_t len) -+{ -+ asm ("mov x0, %0\n" -+ "mov x1, %1\n" -+ "brk #0x69" :: "r" (addr), "r" (len) : "x0", "x1"); -+} -+#endif -+ - static int alloc_code_gen_buffer_splitwx_vmremap(size_t size, Error **errp) - { - kern_return_t ret; - mach_vm_address_t buf_rw, buf_rx; - vm_prot_t cur_prot, max_prot; -+ int orig_prot = PROT_READ | PROT_WRITE; -+ -+#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR -+ /* iOS 26 with TXM requires new workaround*/ -+ if (__builtin_available(iOS 26, visionOS 26, watchOS 26, tvOS 26, *)) { -+ orig_prot = PROT_READ | PROT_EXEC; -+ } -+#endif - -- /* Map the read-write portion via normal anon memory. */ -- if (!alloc_code_gen_buffer_anon(size, PROT_READ | PROT_WRITE, -+ if (!alloc_code_gen_buffer_anon(size, orig_prot, - MAP_PRIVATE | MAP_ANONYMOUS, errp)) { - return -1; - } -@@ -662,6 +705,23 @@ static int alloc_code_gen_buffer_splitwx_vmremap(size_t size, Error **errp) - return -1; - } - -+#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR -+ if (__builtin_available(iOS 26, visionOS 26, watchOS 26, tvOS 26, *)) { -+ if (is_debugger_attached()) { -+ /* let debugger modify the page permission */ -+ break_prepare_jit_region(buf_rx, size); -+ } -+ -+ /* finally mark the read-write portion as RW */ -+ if (mprotect((void *)buf_rw, size, PROT_READ | PROT_WRITE) != 0) { -+ error_setg_errno(errp, errno, "mprotect for jit splitwx (rw)"); -+ munmap((void *)buf_rx, size); -+ munmap((void *)buf_rw, size); -+ return -1; -+ } -+ } -+#endif -+ - tcg_splitwx_diff = buf_rx - buf_rw; - return PROT_READ | PROT_WRITE; - } --- -2.41.0 - -From 338fdd995e8f665edbdb1acd0f1b63f2ca3c266c Mon Sep 17 00:00:00 2001 -From: osy -Date: Fri, 2 Jan 2026 12:41:52 -0800 -Subject: [PATCH] ui/spice-display: fix typo in spice_iosurface_resize - ---- - ui/spice-display.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/ui/spice-display.c b/ui/spice-display.c -index b47722ea4f..c7688256e9 100644 ---- a/ui/spice-display.c -+++ b/ui/spice-display.c -@@ -912,8 +912,8 @@ static void spice_iosurface_destroy(SimpleSpiceDisplay *ssd) - static int spice_iosurface_resize(SimpleSpiceDisplay *ssd, int width, int height) - { - if (ssd->iosurface) { -- if (IOSurfaceGetHeight(ssd->iosurface) != width || -- IOSurfaceGetWidth(ssd->iosurface) != height) { -+ if (IOSurfaceGetHeight(ssd->iosurface) != height || -+ IOSurfaceGetWidth(ssd->iosurface) != width) { - spice_iosurface_destroy(ssd); - return spice_iosurface_create(ssd, width, height); - } else { --- -2.50.1 (Apple Git-155) - -From cde37cd6ddc4fa4e6ffe6151ef182147d676b87a Mon Sep 17 00:00:00 2001 -From: osy -Date: Sat, 8 Nov 2025 16:18:44 -0800 -Subject: [PATCH 1/9] Revert "Use virgl_renderer_borrow_texture_for_scanout" - -This reverts commit 8b9e3062a4c2e243f0f80482385ebccbb8357f71. ---- - hw/display/virtio-gpu-virgl.c | 105 ++++++++-------------------------- - include/ui/console.h | 25 ++++---- - include/ui/gtk.h | 14 +++-- - include/ui/sdl2.h | 7 ++- - include/ui/spice-display.h | 4 +- - ui/console.c | 25 +++++--- - ui/dbus-console.c | 7 ++- - ui/dbus-listener.c | 23 +------- - ui/egl-headless.c | 23 +------- - ui/gtk-egl.c | 27 ++------- - ui/gtk-gl-area.c | 24 +------- - ui/sdl2-gl.c | 17 ++---- - ui/spice-display.c | 36 +++++------- - 13 files changed, 107 insertions(+), 230 deletions(-) - -diff --git a/hw/display/virtio-gpu-virgl.c b/hw/display/virtio-gpu-virgl.c -index d78c2e4892..145a0b3879 100644 ---- a/hw/display/virtio-gpu-virgl.c -+++ b/hw/display/virtio-gpu-virgl.c -@@ -396,87 +396,11 @@ static void virgl_cmd_resource_flush(VirtIOGPU *g, - } - } - --static GLuint virgl_borrow_texture_for_scanout(uint32_t id, bool *y_0_top, -- uint32_t *width, -- uint32_t *height, -- void **d3d_tex2d) --{ -- struct virgl_renderer_texture_info info; -- int ret; -- -- memset(&info, 0, sizeof(info)); -- -- ret = virgl_renderer_borrow_texture_for_scanout(id, &info); -- if (ret == -1) { -- return 0; -- } -- -- if (y_0_top) { -- *y_0_top = info.flags & VIRTIO_GPU_RESOURCE_FLAG_Y_0_TOP; -- } -- -- if (width) { -- *width = info.width; -- } -- -- if (height) { -- *height = info.height; -- } -- -- if (d3d_tex2d) { -- *d3d_tex2d = NULL; -- } -- -- return info.tex_id; --} -- --#if VIRGL_VERSION_MAJOR >= 1 --static GLuint virgl_borrow_d3d_info_for_scanout(uint32_t id, bool *y_0_top, -- uint32_t *width, -- uint32_t *height, -- void **d3d_tex2d) --{ -- int ret; -- struct virgl_renderer_resource_info info; -- struct virgl_renderer_resource_info_ext ext; -- void *d3d_tex2d = NULL; -- -- memset(&ext, 0, sizeof(ext)); -- -- ret = virgl_renderer_resource_get_info_ext(id, &ext); -- info = ext.base; -- d3d_tex2d = ext.d3d_tex2d; -- if (ret) { -- qemu_log_mask(LOG_GUEST_ERROR, -- "%s: illegal resource specified %d\n", -- __func__, id); -- return 0; -- } -- -- if (y_0_top) { -- *y_0_top = info.flags & VIRTIO_GPU_RESOURCE_FLAG_Y_0_TOP; -- } -- -- if (width) { -- *width = info.width; -- } -- -- if (height) { -- *height = info.height; -- } -- -- if (d3d_tex2d) { -- *d3d_tex2d = ext.d3d_tex2d; -- } -- -- return info.tex_id; --} --#endif -- - static void virgl_cmd_set_scanout(VirtIOGPU *g, - struct virtio_gpu_ctrl_command *cmd) - { - struct virtio_gpu_set_scanout ss; -+ int ret; - - VIRTIO_GPU_FILL_CMD(ss); - trace_virtio_gpu_cmd_set_scanout(ss.scanout_id, ss.resource_id, -@@ -491,18 +415,35 @@ static void virgl_cmd_set_scanout(VirtIOGPU *g, - g->parent_obj.enable = 1; - - if (ss.resource_id && ss.r.width && ss.r.height) { -- DisplayGLTextureBorrower borrower; -+ struct virgl_renderer_resource_info info; -+ void *d3d_tex2d = NULL; -+ - #if VIRGL_VERSION_MAJOR >= 1 -- borrower = virgl_borrow_d3d_info_for_scanout; -+ struct virgl_renderer_resource_info_ext ext; -+ memset(&ext, 0, sizeof(ext)); -+ ret = virgl_renderer_resource_get_info_ext(ss.resource_id, &ext); -+ info = ext.base; -+ d3d_tex2d = ext.d3d_tex2d; - #else -- borrower = virgl_borrow_texture_for_scanout; -+ memset(&info, 0, sizeof(info)); -+ ret = virgl_renderer_resource_get_info(ss.resource_id, &info); - #endif -+ if (ret) { -+ qemu_log_mask(LOG_GUEST_ERROR, -+ "%s: illegal resource specified %d\n", -+ __func__, ss.resource_id); -+ cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; -+ return; -+ } - qemu_console_resize(g->parent_obj.scanout[ss.scanout_id].con, - ss.r.width, ss.r.height); - virgl_renderer_force_ctx_0(); - dpy_gl_scanout_texture( -- g->parent_obj.scanout[ss.scanout_id].con, ss.resource_id, -- borrower, ss.r.x, ss.r.y, ss.r.width, ss.r.height); -+ g->parent_obj.scanout[ss.scanout_id].con, info.tex_id, -+ info.flags & VIRTIO_GPU_RESOURCE_FLAG_Y_0_TOP, -+ info.width, info.height, -+ ss.r.x, ss.r.y, ss.r.width, ss.r.height, -+ d3d_tex2d); - } else { - dpy_gfx_replace_surface( - g->parent_obj.scanout[ss.scanout_id].con, NULL); -diff --git a/include/ui/console.h b/include/ui/console.h -index 8717931ed3..46b3128185 100644 ---- a/include/ui/console.h -+++ b/include/ui/console.h -@@ -131,18 +131,16 @@ struct QemuConsoleClass { - ObjectClass parent_class; - }; - --typedef uint32_t (* DisplayGLTextureBorrower)(uint32_t id, bool *y_0_top, -- uint32_t *width, -- uint32_t *height, -- void **d3d_tex2d); -- - typedef struct ScanoutTexture { - uint32_t backing_id; -- DisplayGLTextureBorrower backing_borrow; -+ bool backing_y_0_top; -+ uint32_t backing_width; -+ uint32_t backing_height; - uint32_t x; - uint32_t y; - uint32_t width; - uint32_t height; -+ void *d3d_tex2d; - } ScanoutTexture; - - typedef struct QemuUIInfo { -@@ -242,9 +240,12 @@ typedef struct DisplayChangeListenerOps { - /* required if GL */ - void (*dpy_gl_scanout_texture)(DisplayChangeListener *dcl, - uint32_t backing_id, -- DisplayGLTextureBorrower backing_borrow, -+ bool backing_y_0_top, -+ uint32_t backing_width, -+ uint32_t backing_height, - uint32_t x, uint32_t y, -- uint32_t w, uint32_t h); -+ uint32_t w, uint32_t h, -+ void *d3d_tex2d); - /* optional (default to true if has dpy_gl_scanout_dmabuf) */ - bool (*dpy_has_dmabuf)(DisplayChangeListener *dcl); - /* optional */ -@@ -324,9 +325,11 @@ bool dpy_gfx_check_format(QemuConsole *con, - pixman_format_code_t format); - - void dpy_gl_scanout_disable(QemuConsole *con); --void dpy_gl_scanout_texture(QemuConsole *con, uint32_t backing_id, -- DisplayGLTextureBorrower backing_borrow, -- uint32_t x, uint32_t y, uint32_t w, uint32_t h); -+void dpy_gl_scanout_texture(QemuConsole *con, -+ uint32_t backing_id, bool backing_y_0_top, -+ uint32_t backing_width, uint32_t backing_height, -+ uint32_t x, uint32_t y, uint32_t w, uint32_t h, -+ void *d3d_tex2d); - void dpy_gl_scanout_dmabuf(QemuConsole *con, - QemuDmaBuf *dmabuf); - void dpy_gl_cursor_dmabuf(QemuConsole *con, QemuDmaBuf *dmabuf, -diff --git a/include/ui/gtk.h b/include/ui/gtk.h -index bfd1075b6d..aa3d637029 100644 ---- a/include/ui/gtk.h -+++ b/include/ui/gtk.h -@@ -171,9 +171,12 @@ QEMUGLContext gd_egl_create_context(DisplayGLCtx *dgc, - void gd_egl_scanout_disable(DisplayChangeListener *dcl); - void gd_egl_scanout_texture(DisplayChangeListener *dcl, - uint32_t backing_id, -- DisplayGLTextureBorrower backing_borrow, -+ bool backing_y_0_top, -+ uint32_t backing_width, -+ uint32_t backing_height, - uint32_t x, uint32_t y, -- uint32_t w, uint32_t h); -+ uint32_t w, uint32_t h, -+ void *d3d_tex2d); - void gd_egl_scanout_dmabuf(DisplayChangeListener *dcl, - QemuDmaBuf *dmabuf); - void gd_egl_cursor_dmabuf(DisplayChangeListener *dcl, -@@ -205,9 +208,12 @@ void gd_gl_area_scanout_dmabuf(DisplayChangeListener *dcl, - QemuDmaBuf *dmabuf); - void gd_gl_area_scanout_texture(DisplayChangeListener *dcl, - uint32_t backing_id, -- DisplayGLTextureBorrower backing_borrow, -+ bool backing_y_0_top, -+ uint32_t backing_width, -+ uint32_t backing_height, - uint32_t x, uint32_t y, -- uint32_t w, uint32_t h); -+ uint32_t w, uint32_t h, -+ void *d3d_tex2d); - void gd_gl_area_scanout_disable(DisplayChangeListener *dcl); - void gd_gl_area_scanout_flush(DisplayChangeListener *dcl, - uint32_t x, uint32_t y, uint32_t w, uint32_t h); -diff --git a/include/ui/sdl2.h b/include/ui/sdl2.h -index c079f9c5e0..dbe6e3d973 100644 ---- a/include/ui/sdl2.h -+++ b/include/ui/sdl2.h -@@ -88,9 +88,12 @@ int sdl2_gl_make_context_current(DisplayGLCtx *dgc, - void sdl2_gl_scanout_disable(DisplayChangeListener *dcl); - void sdl2_gl_scanout_texture(DisplayChangeListener *dcl, - uint32_t backing_id, -- DisplayGLTextureBorrower backing_borrow, -+ bool backing_y_0_top, -+ uint32_t backing_width, -+ uint32_t backing_height, - uint32_t x, uint32_t y, -- uint32_t w, uint32_t h); -+ uint32_t w, uint32_t h, -+ void *d3d_tex2d); - void sdl2_gl_scanout_flush(DisplayChangeListener *dcl, - uint32_t x, uint32_t y, uint32_t w, uint32_t h); - -diff --git a/include/ui/spice-display.h b/include/ui/spice-display.h -index 6f8305888d..4b9121bfb5 100644 ---- a/include/ui/spice-display.h -+++ b/include/ui/spice-display.h -@@ -141,8 +141,8 @@ struct SimpleSpiceDisplay { - #if defined(CONFIG_ANGLE) - EGLSurface esurface; - egl_fb iosurface_fb; -- DisplayGLTextureBorrower backing_borrow; -- uint32_t backing_id; -+ GLuint tex_id; -+ bool y_0_top; - #endif - bool render_cursor; - -diff --git a/ui/console.c b/ui/console.c -index 4a0d205d73..480cd63f90 100644 ---- a/ui/console.c -+++ b/ui/console.c -@@ -288,11 +288,14 @@ static void displaychangelistener_display_console(DisplayChangeListener *dcl, - dcl->ops->dpy_gl_scanout_texture) { - dcl->ops->dpy_gl_scanout_texture(dcl, - con->scanout.texture.backing_id, -- con->scanout.texture.backing_borrow, -+ con->scanout.texture.backing_y_0_top, -+ con->scanout.texture.backing_width, -+ con->scanout.texture.backing_height, - con->scanout.texture.x, - con->scanout.texture.y, - con->scanout.texture.width, -- con->scanout.texture.height); -+ con->scanout.texture.height, -+ con->scanout.texture.d3d_tex2d); - } - } - -@@ -1015,25 +1018,31 @@ void dpy_gl_scanout_disable(QemuConsole *con) - - void dpy_gl_scanout_texture(QemuConsole *con, - uint32_t backing_id, -- DisplayGLTextureBorrower backing_borrow, -+ bool backing_y_0_top, -+ uint32_t backing_width, -+ uint32_t backing_height, - uint32_t x, uint32_t y, -- uint32_t width, uint32_t height) -+ uint32_t width, uint32_t height, -+ void *d3d_tex2d) - { - DisplayState *s = con->ds; - DisplayChangeListener *dcl; - - con->scanout.kind = SCANOUT_TEXTURE; - con->scanout.texture = (ScanoutTexture) { -- backing_id, backing_borrow, -- x, y, width, height -+ backing_id, backing_y_0_top, backing_width, backing_height, -+ x, y, width, height, d3d_tex2d, - }; - QLIST_FOREACH(dcl, &s->listeners, next) { - if (con != dcl->con) { - continue; - } - if (dcl->ops->dpy_gl_scanout_texture) { -- dcl->ops->dpy_gl_scanout_texture(dcl, backing_id, backing_borrow, -- x, y, width, height); -+ dcl->ops->dpy_gl_scanout_texture(dcl, backing_id, -+ backing_y_0_top, -+ backing_width, backing_height, -+ x, y, width, height, -+ d3d_tex2d); - } - } - } -diff --git a/ui/dbus-console.c b/ui/dbus-console.c -index 31939d0207..85e215ef23 100644 ---- a/ui/dbus-console.c -+++ b/ui/dbus-console.c -@@ -94,9 +94,12 @@ dbus_gl_scanout_disable(DisplayChangeListener *dcl) - static void - dbus_gl_scanout_texture(DisplayChangeListener *dcl, - uint32_t tex_id, -- DisplayGLTextureBorrower backing_borrow, -+ bool backing_y_0_top, -+ uint32_t backing_width, -+ uint32_t backing_height, - uint32_t x, uint32_t y, -- uint32_t w, uint32_t h) -+ uint32_t w, uint32_t h, -+ void *d3d_tex2d) - { - DBusDisplayConsole *ddc = container_of(dcl, DBusDisplayConsole, dcl); - -diff --git a/ui/dbus-listener.c b/ui/dbus-listener.c -index abcc96287b..51244c9240 100644 ---- a/ui/dbus-listener.c -+++ b/ui/dbus-listener.c -@@ -491,7 +491,7 @@ static bool dbus_scanout_map(DBusDisplayListener *ddl) - #endif /* WIN32 */ - - #ifdef CONFIG_OPENGL --static void dbus_scanout_borrowed_texture(DisplayChangeListener *dcl, -+static void dbus_scanout_texture(DisplayChangeListener *dcl, - uint32_t tex_id, - bool backing_y_0_top, - uint32_t backing_width, -@@ -540,25 +540,6 @@ static void dbus_scanout_borrowed_texture(DisplayChangeListener *dcl, - #endif - } - --static void dbus_scanout_texture(DisplayChangeListener *dcl, -- uint32_t backing_id, -- DisplayGLTextureBorrower backing_borrow, -- uint32_t x, uint32_t y, -- uint32_t w, uint32_t h) --{ -- bool backing_y_0_top; -- uint32_t backing_width; -- uint32_t backing_height; -- void *d3d_tex2d; -- uint32_t tex_id = backing_borrow(backing_id, &backing_y_0_top, -- &backing_width, &backing_height, -- &d3d_tex2d); -- -- dbus_scanout_borrowed_texture(dcl, tex_id, backing_y_0_top, -- backing_width, backing_height, -- x, y, w, h, d3d_tex2d); --} -- - #ifdef CONFIG_GBM - static void dbus_cursor_dmabuf(DisplayChangeListener *dcl, - QemuDmaBuf *dmabuf, bool have_hot, -@@ -798,7 +779,7 @@ static void dbus_gl_gfx_switch(DisplayChangeListener *dcl, - int height = surface_height(ddl->ds); - - /* TODO: lazy send dmabuf (there are unnecessary sent otherwise) */ -- dbus_scanout_borrowed_texture(&ddl->dcl, ddl->ds->texture, false, -+ dbus_scanout_texture(&ddl->dcl, ddl->ds->texture, false, - width, height, 0, 0, width, height, NULL); - } - } -diff --git a/ui/egl-headless.c b/ui/egl-headless.c -index 96bbf52fd0..ba8d099f80 100644 ---- a/ui/egl-headless.c -+++ b/ui/egl-headless.c -@@ -63,7 +63,7 @@ static void egl_scanout_disable(DisplayChangeListener *dcl) - egl_fb_destroy(&edpy->blit_fb); - } - --static void egl_scanout_imported_texture(DisplayChangeListener *dcl, -+static void egl_scanout_texture(DisplayChangeListener *dcl, - uint32_t backing_id, - bool backing_y_0_top, - uint32_t backing_width, -@@ -88,25 +88,6 @@ static void egl_scanout_imported_texture(DisplayChangeListener *dcl, - } - } - --static void egl_scanout_texture(DisplayChangeListener *dcl, -- uint32_t backing_id, -- DisplayGLTextureBorrower backing_borrow, -- uint32_t x, uint32_t y, -- uint32_t w, uint32_t h) --{ -- bool backing_y_0_top; -- uint32_t backing_width; -- uint32_t backing_height; -- void *d3d_tex2d; -- -- GLuint backing_texture = backing_borrow(backing_id, &backing_y_0_top, -- &backing_width, &backing_height, -- &d3d_tex2d); -- egl_scanout_imported_texture(dcl, backing_texture, backing_y_0_top, -- backing_width, backing_height, -- x, y, w, h, d3d_tex2d); --} -- - #ifdef CONFIG_GBM - - static void egl_scanout_dmabuf(DisplayChangeListener *dcl, -@@ -123,7 +104,7 @@ static void egl_scanout_dmabuf(DisplayChangeListener *dcl, - width = qemu_dmabuf_get_width(dmabuf); - height = qemu_dmabuf_get_height(dmabuf); - -- egl_scanout_imported_texture(dcl, texture, false, width, height, 0, 0, -+ egl_scanout_texture(dcl, texture, false, width, height, 0, 0, - width, height, NULL); - } - -diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c -index d600456159..341afd41fb 100644 ---- a/ui/gtk-egl.c -+++ b/ui/gtk-egl.c -@@ -233,7 +233,7 @@ void gd_egl_scanout_disable(DisplayChangeListener *dcl) - gtk_egl_set_scanout_mode(vc, false); - } - --void gd_egl_scanout_borrowed_texture(DisplayChangeListener *dcl, -+void gd_egl_scanout_texture(DisplayChangeListener *dcl, - uint32_t backing_id, bool backing_y_0_top, - uint32_t backing_width, uint32_t backing_height, - uint32_t x, uint32_t y, -@@ -263,25 +263,8 @@ void gd_egl_scanout_borrowed_texture(DisplayChangeListener *dcl, - backing_id, false); - } - --void gd_egl_scanout_texture(DisplayChangeListener *dcl, uint32_t backing_id, -- DisplayGLTextureBorrower backing_borrow, -- uint32_t x, uint32_t y, -- uint32_t w, uint32_t h) --{ -- bool backing_y_0_top; -- uint32_t backing_width; -- uint32_t backing_height; -- void *d3d_tex2d; -- -- GLuint backing_texture = backing_borrow(backing_id, &backing_y_0_top, -- &backing_width, &backing_height, -- &d3d_tex2d); -- gd_egl_scanout_borrowed_texture(dcl, backing_texture, backing_y_0_top, -- backing_width, backing_height, -- x, y, w, h, d3d_tex2d); --} -- --void gd_egl_scanout_dmabuf(DisplayChangeListener *dcl, QemuDmaBuf *dmabuf) -+void gd_egl_scanout_dmabuf(DisplayChangeListener *dcl, -+ QemuDmaBuf *dmabuf) - { - #ifdef CONFIG_GBM - VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); -@@ -305,8 +288,8 @@ void gd_egl_scanout_dmabuf(DisplayChangeListener *dcl, QemuDmaBuf *dmabuf) - backing_height = qemu_dmabuf_get_backing_height(dmabuf); - y0_top = qemu_dmabuf_get_y0_top(dmabuf); - -- gd_egl_scanout_borrowed_texture(dcl, texture, y0_top, backing_width, -- backing_height, x, y, width, height, NULL); -+ gd_egl_scanout_texture(dcl, texture, y0_top, backing_width, backing_height, -+ x, y, width, height, NULL); - - if (qemu_dmabuf_get_allow_fences(dmabuf)) { - vc->gfx.guest_fb.dmabuf = dmabuf; -diff --git a/ui/gtk-gl-area.c b/ui/gtk-gl-area.c -index 32bca91cbb..2c9a0db425 100644 ---- a/ui/gtk-gl-area.c -+++ b/ui/gtk-gl-area.c -@@ -248,7 +248,7 @@ void gd_gl_area_destroy_context(DisplayGLCtx *dgc, QEMUGLContext ctx) - g_clear_object(&ctx); - } - --void gd_gl_area_scanout_borrowed_texture(DisplayChangeListener *dcl, -+void gd_gl_area_scanout_texture(DisplayChangeListener *dcl, - uint32_t backing_id, - bool backing_y_0_top, - uint32_t backing_width, -@@ -277,26 +277,6 @@ void gd_gl_area_scanout_borrowed_texture(DisplayChangeListener *dcl, - backing_id, false); - } - --void gd_gl_area_scanout_texture(DisplayChangeListener *dcl, -- uint32_t backing_id, -- DisplayGLTextureBorrower backing_borrow, -- uint32_t x, uint32_t y, -- uint32_t w, uint32_t h) --{ -- bool backing_y_0_top; -- uint32_t backing_width; -- uint32_t backing_height; -- void *d3d_tex2d; -- -- GLuint backing_texture = backing_borrow(backing_id, &backing_y_0_top, -- &backing_width, &backing_height, -- &d3d_tex2d); -- gd_gl_area_scanout_borrowed_texture(dcl, backing_texture, -- backing_y_0_top, -- backing_width, backing_height, -- x, y, w, h, d3d_tex2d); --} -- - void gd_gl_area_scanout_disable(DisplayChangeListener *dcl) - { - VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); -@@ -341,7 +321,7 @@ void gd_gl_area_scanout_dmabuf(DisplayChangeListener *dcl, - backing_height = qemu_dmabuf_get_backing_height(dmabuf); - y0_top = qemu_dmabuf_get_y0_top(dmabuf); - -- gd_gl_area_scanout_borrowed_texture(dcl, texture, y0_top, -+ gd_gl_area_scanout_texture(dcl, texture, y0_top, - backing_width, backing_height, - x, y, width, height, NULL); - -diff --git a/ui/sdl2-gl.c b/ui/sdl2-gl.c -index b164fb6b28..061f10c449 100644 ---- a/ui/sdl2-gl.c -+++ b/ui/sdl2-gl.c -@@ -201,22 +201,17 @@ void sdl2_gl_scanout_disable(DisplayChangeListener *dcl) - - void sdl2_gl_scanout_texture(DisplayChangeListener *dcl, - uint32_t backing_id, -- DisplayGLTextureBorrower backing_borrow, -+ bool backing_y_0_top, -+ uint32_t backing_width, -+ uint32_t backing_height, - uint32_t x, uint32_t y, -- uint32_t w, uint32_t h) -+ uint32_t w, uint32_t h, -+ void *d3d_tex2d) - { - struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl); -- bool backing_y_0_top; -- uint32_t backing_width; -- uint32_t backing_height; -- void *d3d_tex2d; - - assert(scon->opengl); - -- GLuint backing_texture = backing_borrow(backing_id, &backing_y_0_top, -- &backing_width, &backing_height, -- &d3d_tex2d); -- - scon->x = x; - scon->y = y; - scon->w = w; -@@ -227,7 +222,7 @@ void sdl2_gl_scanout_texture(DisplayChangeListener *dcl, - - sdl2_set_scanout_mode(scon, true); - egl_fb_setup_for_tex(&scon->guest_fb, backing_width, backing_height, -- backing_texture, false); -+ backing_id, false); - } - - void sdl2_gl_scanout_flush(DisplayChangeListener *dcl, -diff --git a/ui/spice-display.c b/ui/spice-display.c -index c7688256e9..61cdda0e8a 100644 ---- a/ui/spice-display.c -+++ b/ui/spice-display.c -@@ -1132,36 +1132,30 @@ static void qemu_spice_gl_scanout_disable(DisplayChangeListener *dcl) - spice_iosurface_destroy(ssd); - #endif - #if defined(CONFIG_ANGLE) -- ssd->backing_borrow = NULL; -- ssd->backing_id = -1; -+ ssd->tex_id = -1; - #endif - } - - static void qemu_spice_gl_scanout_texture(DisplayChangeListener *dcl, -- uint32_t backing_id, -- DisplayGLTextureBorrower backing_borrow, -+ uint32_t tex_id, -+ bool y_0_top, -+ uint32_t backing_width, -+ uint32_t backing_height, - uint32_t x, uint32_t y, -- uint32_t w, uint32_t h) -+ uint32_t w, uint32_t h, -+ void *d3d_tex2d) - { - SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); - EGLint stride = 0, fourcc = 0; - int fd = -1; -- bool y_0_top; -- uint32_t backing_width; -- uint32_t backing_height; -- void *d3d_tex2d; -- -- GLuint tex_id = backing_borrow(backing_id, &y_0_top, -- &backing_width, &backing_height, -- &d3d_tex2d); -- assert(tex_id); -+ - #if defined(CONFIG_GBM) - fd = egl_get_fd_for_texture(tex_id, &stride, &fourcc, NULL); - #elif defined(CONFIG_IOSURFACE) - if (spice_iosurface_resize(ssd, backing_width, backing_height)) { - #if defined(CONFIG_ANGLE) -- ssd->backing_borrow = backing_borrow; -- ssd->backing_id = backing_id; -+ ssd->tex_id = tex_id; -+ ssd->y_0_top = y_0_top; - #endif - fd = spice_iosurface_create_fd(ssd, &fourcc); - } else { -@@ -1253,11 +1247,10 @@ static void qemu_spice_gl_update(DisplayChangeListener *dcl, - EGLint stride = 0, fourcc = 0; - int fd; - bool render_cursor = false; -+ uint32_t texture; - #endif - bool y_0_top = false; /* FIXME */ - uint64_t cookie; -- int fd; -- uint32_t width, height, texture; - - if (!ssd->have_scanout) { - return; -@@ -1331,8 +1324,8 @@ static void qemu_spice_gl_update(DisplayChangeListener *dcl, - glFlush(); - } - #elif defined(CONFIG_ANGLE) && defined(CONFIG_IOSURFACE) -- GLuint tex_id = ssd->backing_borrow(ssd->backing_id, &y_0_top, -- NULL, NULL, NULL); -+ GLuint tex_id = ssd->tex_id; -+ y_0_top = ssd->y_0_top; - spice_iosurface_blit(ssd, tex_id, !y_0_top, false); - //TODO: cursor stuff - #endif -@@ -1403,8 +1396,7 @@ static void qemu_spice_display_init_one(QemuConsole *con) - #endif - #if defined(CONFIG_ANGLE) - ssd->esurface = EGL_NO_SURFACE; -- ssd->backing_borrow = NULL; -- ssd->backing_id = -1; -+ ssd->tex_id = -1; - #endif - } - #endif --- -2.50.1 (Apple Git-155) - -From 11c36e637e854a63d07dc902e82378c8e2a9071f Mon Sep 17 00:00:00 2001 -From: Joelle van Dyne -Date: Sat, 29 Nov 2025 22:53:18 -0800 -Subject: [PATCH 2/9] egl-helpers: store handle to native device - -Make way for other platforms by making the variable more general. Also we -will be using the device in the future so let's save the pointer in the -global instead of just a boolean flag. - -Signed-off-by: Joelle van Dyne ---- - hw/display/virtio-gpu-virgl.c | 2 +- - include/ui/egl-helpers.h | 2 +- - ui/egl-helpers.c | 4 ++-- - 3 files changed, 4 insertions(+), 4 deletions(-) - -diff --git a/hw/display/virtio-gpu-virgl.c b/hw/display/virtio-gpu-virgl.c -index 145a0b3879..0511c6a478 100644 ---- a/hw/display/virtio-gpu-virgl.c -+++ b/hw/display/virtio-gpu-virgl.c -@@ -1104,7 +1104,7 @@ int virtio_gpu_virgl_init(VirtIOGPU *g) - } - #endif - #ifdef VIRGL_RENDERER_D3D11_SHARE_TEXTURE -- if (qemu_egl_angle_d3d) { -+ if (qemu_egl_angle_native_device) { - flags |= VIRGL_RENDERER_D3D11_SHARE_TEXTURE; - } - #endif -diff --git a/include/ui/egl-helpers.h b/include/ui/egl-helpers.h -index eb08ef1990..2930aef7e3 100644 ---- a/include/ui/egl-helpers.h -+++ b/include/ui/egl-helpers.h -@@ -15,7 +15,7 @@ - extern EGLDisplay *qemu_egl_display; - extern EGLConfig qemu_egl_config; - extern DisplayGLMode qemu_egl_mode; --extern bool qemu_egl_angle_d3d; -+extern void *qemu_egl_angle_native_device; - - typedef struct egl_fb { - int width; -diff --git a/ui/egl-helpers.c b/ui/egl-helpers.c -index 7b403b87a5..42d2d511b1 100644 ---- a/ui/egl-helpers.c -+++ b/ui/egl-helpers.c -@@ -27,7 +27,7 @@ - EGLDisplay *qemu_egl_display; - EGLConfig qemu_egl_config; - DisplayGLMode qemu_egl_mode; --bool qemu_egl_angle_d3d; -+void *qemu_egl_angle_native_device; - - /* ------------------------------------------------------------------ */ - -@@ -640,7 +640,7 @@ int qemu_egl_init_dpy_win32(EGLNativeDisplayType dpy, DisplayGLMode mode) - } - - trace_egl_init_d3d11_device(device); -- qemu_egl_angle_d3d = device != NULL; -+ qemu_egl_angle_native_device = d3d11_device; - } - #endif - --- -2.41.0 - -From e02ff2b56fedb74e1161498950b55d2284a2788d Mon Sep 17 00:00:00 2001 -From: Joelle van Dyne -Date: Sat, 3 Jan 2026 14:32:15 -0800 -Subject: [PATCH 3/9] console: rename `d3d_tex2d` to `native` - -In order to support native texture scanout beyond D3D, we make this more -generic allowing for multiple native texture handle types. - -Signed-off-by: Joelle van Dyne ---- - hw/display/virtio-gpu-virgl.c | 10 +++++++--- - include/ui/console.h | 20 +++++++++++++++++--- - include/ui/gtk.h | 4 ++-- - include/ui/sdl2.h | 2 +- - ui/console.c | 8 ++++---- - ui/dbus-console.c | 2 +- - ui/dbus-listener.c | 8 ++++---- - ui/egl-headless.c | 4 ++-- - ui/gtk-egl.c | 4 ++-- - ui/gtk-gl-area.c | 4 ++-- - ui/sdl2-gl.c | 2 +- - ui/spice-display.c | 2 +- - 12 files changed, 44 insertions(+), 26 deletions(-) - -diff --git a/hw/display/virtio-gpu-virgl.c b/hw/display/virtio-gpu-virgl.c -index 0511c6a478..eac49fbae8 100644 ---- a/hw/display/virtio-gpu-virgl.c -+++ b/hw/display/virtio-gpu-virgl.c -@@ -416,14 +416,18 @@ static void virgl_cmd_set_scanout(VirtIOGPU *g, - - if (ss.resource_id && ss.r.width && ss.r.height) { - struct virgl_renderer_resource_info info; -- void *d3d_tex2d = NULL; -+ ScanoutTextureNative native = NO_NATIVE_TEXTURE; - - #if VIRGL_VERSION_MAJOR >= 1 - struct virgl_renderer_resource_info_ext ext; - memset(&ext, 0, sizeof(ext)); - ret = virgl_renderer_resource_get_info_ext(ss.resource_id, &ext); - info = ext.base; -- d3d_tex2d = ext.d3d_tex2d; -+ native = (ScanoutTextureNative){ -+ .type = ext.d3d_tex2d ? SCANOUT_TEXTURE_NATIVE_TYPE_D3D : -+ SCANOUT_TEXTURE_NATIVE_TYPE_NONE, -+ .handle = ext.d3d_tex2d, -+ }; - #else - memset(&info, 0, sizeof(info)); - ret = virgl_renderer_resource_get_info(ss.resource_id, &info); -@@ -443,7 +447,7 @@ static void virgl_cmd_set_scanout(VirtIOGPU *g, - info.flags & VIRTIO_GPU_RESOURCE_FLAG_Y_0_TOP, - info.width, info.height, - ss.r.x, ss.r.y, ss.r.width, ss.r.height, -- d3d_tex2d); -+ native); - } else { - dpy_gfx_replace_surface( - g->parent_obj.scanout[ss.scanout_id].con, NULL); -diff --git a/include/ui/console.h b/include/ui/console.h -index 46b3128185..445e563150 100644 ---- a/include/ui/console.h -+++ b/include/ui/console.h -@@ -131,6 +131,20 @@ struct QemuConsoleClass { - ObjectClass parent_class; - }; - -+typedef enum ScanoutTextureNativeType { -+ SCANOUT_TEXTURE_NATIVE_TYPE_NONE, -+ SCANOUT_TEXTURE_NATIVE_TYPE_D3D, -+} ScanoutTextureNativeType; -+ -+typedef struct ScanoutTextureNative { -+ ScanoutTextureNativeType type; -+ void *handle; -+} ScanoutTextureNative; -+ -+#define NO_NATIVE_TEXTURE ((ScanoutTextureNative){ \ -+ .type = SCANOUT_TEXTURE_NATIVE_TYPE_NONE \ -+}) -+ - typedef struct ScanoutTexture { - uint32_t backing_id; - bool backing_y_0_top; -@@ -140,7 +154,7 @@ typedef struct ScanoutTexture { - uint32_t y; - uint32_t width; - uint32_t height; -- void *d3d_tex2d; -+ ScanoutTextureNative native; - } ScanoutTexture; - - typedef struct QemuUIInfo { -@@ -245,7 +259,7 @@ typedef struct DisplayChangeListenerOps { - uint32_t backing_height, - uint32_t x, uint32_t y, - uint32_t w, uint32_t h, -- void *d3d_tex2d); -+ ScanoutTextureNative native); - /* optional (default to true if has dpy_gl_scanout_dmabuf) */ - bool (*dpy_has_dmabuf)(DisplayChangeListener *dcl); - /* optional */ -@@ -329,7 +343,7 @@ void dpy_gl_scanout_texture(QemuConsole *con, - uint32_t backing_id, bool backing_y_0_top, - uint32_t backing_width, uint32_t backing_height, - uint32_t x, uint32_t y, uint32_t w, uint32_t h, -- void *d3d_tex2d); -+ ScanoutTextureNative native); - void dpy_gl_scanout_dmabuf(QemuConsole *con, - QemuDmaBuf *dmabuf); - void dpy_gl_cursor_dmabuf(QemuConsole *con, QemuDmaBuf *dmabuf, -diff --git a/include/ui/gtk.h b/include/ui/gtk.h -index aa3d637029..6aacd70ea2 100644 ---- a/include/ui/gtk.h -+++ b/include/ui/gtk.h -@@ -176,7 +176,7 @@ void gd_egl_scanout_texture(DisplayChangeListener *dcl, - uint32_t backing_height, - uint32_t x, uint32_t y, - uint32_t w, uint32_t h, -- void *d3d_tex2d); -+ ScanoutTextureNative native); - void gd_egl_scanout_dmabuf(DisplayChangeListener *dcl, - QemuDmaBuf *dmabuf); - void gd_egl_cursor_dmabuf(DisplayChangeListener *dcl, -@@ -213,7 +213,7 @@ void gd_gl_area_scanout_texture(DisplayChangeListener *dcl, - uint32_t backing_height, - uint32_t x, uint32_t y, - uint32_t w, uint32_t h, -- void *d3d_tex2d); -+ ScanoutTextureNative native); - void gd_gl_area_scanout_disable(DisplayChangeListener *dcl); - void gd_gl_area_scanout_flush(DisplayChangeListener *dcl, - uint32_t x, uint32_t y, uint32_t w, uint32_t h); -diff --git a/include/ui/sdl2.h b/include/ui/sdl2.h -index dbe6e3d973..fdefb88229 100644 ---- a/include/ui/sdl2.h -+++ b/include/ui/sdl2.h -@@ -93,7 +93,7 @@ void sdl2_gl_scanout_texture(DisplayChangeListener *dcl, - uint32_t backing_height, - uint32_t x, uint32_t y, - uint32_t w, uint32_t h, -- void *d3d_tex2d); -+ ScanoutTextureNative native); - void sdl2_gl_scanout_flush(DisplayChangeListener *dcl, - uint32_t x, uint32_t y, uint32_t w, uint32_t h); - -diff --git a/ui/console.c b/ui/console.c -index 480cd63f90..d8d58fc14a 100644 ---- a/ui/console.c -+++ b/ui/console.c -@@ -295,7 +295,7 @@ static void displaychangelistener_display_console(DisplayChangeListener *dcl, - con->scanout.texture.y, - con->scanout.texture.width, - con->scanout.texture.height, -- con->scanout.texture.d3d_tex2d); -+ con->scanout.texture.native); - } - } - -@@ -1023,7 +1023,7 @@ void dpy_gl_scanout_texture(QemuConsole *con, - uint32_t backing_height, - uint32_t x, uint32_t y, - uint32_t width, uint32_t height, -- void *d3d_tex2d) -+ ScanoutTextureNative native) - { - DisplayState *s = con->ds; - DisplayChangeListener *dcl; -@@ -1031,7 +1031,7 @@ void dpy_gl_scanout_texture(QemuConsole *con, - con->scanout.kind = SCANOUT_TEXTURE; - con->scanout.texture = (ScanoutTexture) { - backing_id, backing_y_0_top, backing_width, backing_height, -- x, y, width, height, d3d_tex2d, -+ x, y, width, height, native, - }; - QLIST_FOREACH(dcl, &s->listeners, next) { - if (con != dcl->con) { -@@ -1042,7 +1042,7 @@ void dpy_gl_scanout_texture(QemuConsole *con, - backing_y_0_top, - backing_width, backing_height, - x, y, width, height, -- d3d_tex2d); -+ native); - } - } - } -diff --git a/ui/dbus-console.c b/ui/dbus-console.c -index 85e215ef23..651f0daeaf 100644 ---- a/ui/dbus-console.c -+++ b/ui/dbus-console.c -@@ -99,7 +99,7 @@ dbus_gl_scanout_texture(DisplayChangeListener *dcl, - uint32_t backing_height, - uint32_t x, uint32_t y, - uint32_t w, uint32_t h, -- void *d3d_tex2d) -+ ScanoutTextureNative native) - { - DBusDisplayConsole *ddc = container_of(dcl, DBusDisplayConsole, dcl); - -diff --git a/ui/dbus-listener.c b/ui/dbus-listener.c -index 51244c9240..f5dabaeb85 100644 ---- a/ui/dbus-listener.c -+++ b/ui/dbus-listener.c -@@ -498,7 +498,7 @@ static void dbus_scanout_texture(DisplayChangeListener *dcl, - uint32_t backing_height, - uint32_t x, uint32_t y, - uint32_t w, uint32_t h, -- void *d3d_tex2d) -+ ScanoutTextureNative native) - { - trace_dbus_scanout_texture(tex_id, backing_y_0_top, - backing_width, backing_height, x, y, w, h); -@@ -530,8 +530,8 @@ static void dbus_scanout_texture(DisplayChangeListener *dcl, - assert(surface_width(ddl->ds) == w); - assert(surface_height(ddl->ds) == h); - -- if (d3d_tex2d) { -- dbus_scanout_share_d3d_texture(ddl, d3d_tex2d, backing_y_0_top, -+ if (native.type == SCANOUT_TEXTURE_NATIVE_TYPE_D3D) { -+ dbus_scanout_share_d3d_texture(ddl, native.handle, backing_y_0_top, - backing_width, backing_height, x, y, w, h); - } else { - dbus_scanout_map(ddl); -@@ -780,7 +780,7 @@ static void dbus_gl_gfx_switch(DisplayChangeListener *dcl, - - /* TODO: lazy send dmabuf (there are unnecessary sent otherwise) */ - dbus_scanout_texture(&ddl->dcl, ddl->ds->texture, false, -- width, height, 0, 0, width, height, NULL); -+ width, height, 0, 0, width, height, NO_NATIVE_TEXTURE); - } - } - #endif -diff --git a/ui/egl-headless.c b/ui/egl-headless.c -index ba8d099f80..5a403d13e6 100644 ---- a/ui/egl-headless.c -+++ b/ui/egl-headless.c -@@ -70,7 +70,7 @@ static void egl_scanout_texture(DisplayChangeListener *dcl, - uint32_t backing_height, - uint32_t x, uint32_t y, - uint32_t w, uint32_t h, -- void *d3d_tex2d) -+ ScanoutTextureNative native) - { - egl_dpy *edpy = container_of(dcl, egl_dpy, dcl); - -@@ -105,7 +105,7 @@ static void egl_scanout_dmabuf(DisplayChangeListener *dcl, - height = qemu_dmabuf_get_height(dmabuf); - - egl_scanout_texture(dcl, texture, false, width, height, 0, 0, -- width, height, NULL); -+ width, height, NO_NATIVE_TEXTURE); - } - - static void egl_cursor_dmabuf(DisplayChangeListener *dcl, -diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c -index 341afd41fb..2b94079b15 100644 ---- a/ui/gtk-egl.c -+++ b/ui/gtk-egl.c -@@ -238,7 +238,7 @@ void gd_egl_scanout_texture(DisplayChangeListener *dcl, - uint32_t backing_width, uint32_t backing_height, - uint32_t x, uint32_t y, - uint32_t w, uint32_t h, -- void *d3d_tex2d) -+ ScanoutTextureNative native) - { - VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); - -@@ -289,7 +289,7 @@ void gd_egl_scanout_dmabuf(DisplayChangeListener *dcl, - y0_top = qemu_dmabuf_get_y0_top(dmabuf); - - gd_egl_scanout_texture(dcl, texture, y0_top, backing_width, backing_height, -- x, y, width, height, NULL); -+ x, y, width, height, NO_NATIVE_TEXTURE); - - if (qemu_dmabuf_get_allow_fences(dmabuf)) { - vc->gfx.guest_fb.dmabuf = dmabuf; -diff --git a/ui/gtk-gl-area.c b/ui/gtk-gl-area.c -index 2c9a0db425..f7ed03890d 100644 ---- a/ui/gtk-gl-area.c -+++ b/ui/gtk-gl-area.c -@@ -255,7 +255,7 @@ void gd_gl_area_scanout_texture(DisplayChangeListener *dcl, - uint32_t backing_height, - uint32_t x, uint32_t y, - uint32_t w, uint32_t h, -- void *d3d_tex2d) -+ ScanoutTextureNative native) - { - VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); - -@@ -323,7 +323,7 @@ void gd_gl_area_scanout_dmabuf(DisplayChangeListener *dcl, - - gd_gl_area_scanout_texture(dcl, texture, y0_top, - backing_width, backing_height, -- x, y, width, height, NULL); -+ x, y, width, height, NO_NATIVE_TEXTURE); - - if (qemu_dmabuf_get_allow_fences(dmabuf)) { - vc->gfx.guest_fb.dmabuf = dmabuf; -diff --git a/ui/sdl2-gl.c b/ui/sdl2-gl.c -index 1d8ff3fab0..5b0080a0f9 100644 ---- a/ui/sdl2-gl.c -+++ b/ui/sdl2-gl.c -@@ -206,7 +206,7 @@ void sdl2_gl_scanout_texture(DisplayChangeListener *dcl, - uint32_t backing_height, - uint32_t x, uint32_t y, - uint32_t w, uint32_t h, -- void *d3d_tex2d) -+ ScanoutTextureNative native) - { - struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl); - -diff --git a/ui/spice-display.c b/ui/spice-display.c -index 61cdda0e8a..83a54e69a0 100644 ---- a/ui/spice-display.c -+++ b/ui/spice-display.c -@@ -1143,7 +1143,7 @@ static void qemu_spice_gl_scanout_texture(DisplayChangeListener *dcl, - uint32_t backing_height, - uint32_t x, uint32_t y, - uint32_t w, uint32_t h, -- void *d3d_tex2d) -+ ScanoutTextureNative native) - { - SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); - EGLint stride = 0, fourcc = 0; --- -2.41.0 - -From 270b5d8db6802cc937e2beaf5c87030da1dc39e2 Mon Sep 17 00:00:00 2001 -From: osy -Date: Sun, 4 Jan 2026 13:39:26 -0800 -Subject: [PATCH 4/9] ui/spice-display: move display early init logic to - spice-display.c - ---- - include/ui/qemu-spice.h | 1 + - ui/spice-core.c | 29 +---------------------------- - ui/spice-display.c | 39 +++++++++++++++++++++++++++++++++++++++ - 3 files changed, 41 insertions(+), 28 deletions(-) - -diff --git a/include/ui/qemu-spice.h b/include/ui/qemu-spice.h -index b7d493742c..5f5b9e2147 100644 ---- a/include/ui/qemu-spice.h -+++ b/include/ui/qemu-spice.h -@@ -27,6 +27,7 @@ - #include "qemu/config-file.h" - - void qemu_spice_input_init(void); -+void qemu_spice_display_early_init(void); - void qemu_spice_display_init(void); - void qemu_spice_display_init_done(void); - bool qemu_spice_have_display_interface(QemuConsole *con); -diff --git a/ui/spice-core.c b/ui/spice-core.c -index 1c3b3b395a..43715cb60d 100644 ---- a/ui/spice-core.c -+++ b/ui/spice-core.c -@@ -837,34 +837,7 @@ static void qemu_spice_init(void) - g_free(x509_cacert_file); - g_free(password); - --#ifdef HAVE_SPICE_GL -- if (qemu_opt_get_bool(opts, "gl", 0)) { -- if ((port != 0) || (tls_port != 0)) { -- error_report("SPICE GL support is local-only for now and " -- "incompatible with -spice port/tls-port"); -- exit(1); -- } --#if defined(CONFIG_GBM) -- egl_init(qemu_opt_get(opts, "rendernode"), DISPLAY_GL_MODE_ON, &error_fatal); --#elif defined(CONFIG_ANGLE) -- if (qemu_egl_init_dpy_angle(DISPLAY_GL_MODE_ES)) { -- error_report("SPICE GL failed to initialize ANGLE display"); -- exit(1); -- } -- -- spice_gl_ctx = qemu_egl_init_ctx(); -- if (!spice_gl_ctx) { -- error_report("egl: egl_init_ctx failed"); -- exit(1); -- } --#else -- error_report("No backend to support SPICE GL"); -- exit(1); --#endif -- display_opengl = 1; -- spice_opengl = 1; -- } --#endif -+ qemu_spice_display_early_init(); - } - - static int qemu_spice_add_interface(SpiceBaseInstance *sin) -diff --git a/ui/spice-display.c b/ui/spice-display.c -index 83a54e69a0..7faa922092 100644 ---- a/ui/spice-display.c -+++ b/ui/spice-display.c -@@ -24,6 +24,7 @@ - #include "qemu/option.h" - #include "qemu/queue.h" - #include "ui/console.h" -+#include "system/system.h" - #include "trace.h" - #ifdef CONFIG_IOSURFACE - #include -@@ -1426,6 +1427,44 @@ static void qemu_spice_display_init_one(QemuConsole *con) - register_displaychangelistener(&ssd->dcl); - } - -+void qemu_spice_display_early_init(void) -+{ -+#ifdef HAVE_SPICE_GL -+ QemuOptsList *olist = qemu_find_opts("spice"); -+ QemuOpts *opts = QTAILQ_FIRST(&olist->head); -+ int port, tls_port; -+ -+ port = qemu_opt_get_number(opts, "port", 0); -+ tls_port = qemu_opt_get_number(opts, "tls-port", 0); -+ if (qemu_opt_get_bool(opts, "gl", 0)) { -+ if ((port != 0) || (tls_port != 0)) { -+ error_report("SPICE GL support is local-only for now and " -+ "incompatible with -spice port/tls-port"); -+ exit(1); -+ } -+#if defined(CONFIG_GBM) -+ egl_init(qemu_opt_get(opts, "rendernode"), DISPLAY_GL_MODE_ON, &error_fatal); -+#elif defined(CONFIG_ANGLE) -+ if (qemu_egl_init_dpy_angle(DISPLAY_GL_MODE_ES)) { -+ error_report("SPICE GL failed to initialize ANGLE display"); -+ exit(1); -+ } -+ -+ spice_gl_ctx = qemu_egl_init_ctx(); -+ if (!spice_gl_ctx) { -+ error_report("egl: egl_init_ctx failed"); -+ exit(1); -+ } -+#else -+ error_report("No backend to support SPICE GL"); -+ exit(1); -+#endif -+ display_opengl = 1; -+ spice_opengl = 1; -+ } -+#endif -+} -+ - void qemu_spice_display_init(void) - { - QemuOptsList *olist = qemu_find_opts("spice"); --- -2.41.0 - -From 11b76bf6761f10a586497200745bda9ef2bb9732 Mon Sep 17 00:00:00 2001 -From: osy -Date: Sun, 4 Jan 2026 15:56:00 -0800 -Subject: [PATCH 5/9] Revert "shaders: support byte swapping of pixels" - -This reverts commit e73864e11d4f3224174a677a96925d253ccf892d. ---- - include/ui/egl-helpers.h | 4 ++-- - include/ui/shader.h | 2 +- - include/ui/surface.h | 1 - - ui/console-gl.c | 5 ++--- - ui/egl-headless.c | 4 ++-- - ui/egl-helpers.c | 8 ++++---- - ui/gtk-egl.c | 4 ++-- - ui/shader.c | 26 +++++--------------------- - ui/shader/meson.build | 1 - - ui/shader/texture-blit-swap.frag | 7 ------- - ui/spice-display.c | 10 +++++----- - 11 files changed, 23 insertions(+), 49 deletions(-) - delete mode 100644 ui/shader/texture-blit-swap.frag - -diff --git a/include/ui/egl-helpers.h b/include/ui/egl-helpers.h -index 2930aef7e3..5dd2a6dac0 100644 ---- a/include/ui/egl-helpers.h -+++ b/include/ui/egl-helpers.h -@@ -41,9 +41,9 @@ void egl_fb_blit(egl_fb *dst, egl_fb *src, bool flip); - void egl_fb_read(DisplaySurface *dst, egl_fb *src); - void egl_fb_read_rect(DisplaySurface *dst, egl_fb *src, int x, int y, int w, int h); - --void egl_texture_blit(QemuGLShader *gls, egl_fb *dst, egl_fb *src, bool flip, bool swap); -+void egl_texture_blit(QemuGLShader *gls, egl_fb *dst, egl_fb *src, bool flip); - void egl_texture_blend(QemuGLShader *gls, egl_fb *dst, egl_fb *src, bool flip, -- bool swap, int x, int y, double scale_x, double scale_y); -+ int x, int y, double scale_x, double scale_y); - - extern EGLContext qemu_egl_rn_ctx; - -diff --git a/include/ui/shader.h b/include/ui/shader.h -index 252192793a..4c5acb2ce8 100644 ---- a/include/ui/shader.h -+++ b/include/ui/shader.h -@@ -5,7 +5,7 @@ - - typedef struct QemuGLShader QemuGLShader; - --void qemu_gl_run_texture_blit(QemuGLShader *gls, bool flip, bool swapped); -+void qemu_gl_run_texture_blit(QemuGLShader *gls, bool flip); - - QemuGLShader *qemu_gl_init_shader(void); - void qemu_gl_fini_shader(QemuGLShader *gls); -diff --git a/include/ui/surface.h b/include/ui/surface.h -index 68ce190e26..f16f7be8be 100644 ---- a/include/ui/surface.h -+++ b/include/ui/surface.h -@@ -22,7 +22,6 @@ typedef struct DisplaySurface { - GLenum glformat; - GLenum gltype; - GLuint texture; -- bool glswapped; - #endif - qemu_pixman_shareable share_handle; - uint32_t share_handle_offset; -diff --git a/ui/console-gl.c b/ui/console-gl.c -index 9cdb0bd482..103b954017 100644 ---- a/ui/console-gl.c -+++ b/ui/console-gl.c -@@ -56,9 +56,8 @@ void surface_gl_create_texture(QemuGLShader *gls, - switch (surface_format(surface)) { - case PIXMAN_BE_b8g8r8x8: - case PIXMAN_BE_b8g8r8a8: -- surface->glformat = GL_RGBA; -+ surface->glformat = GL_BGRA_EXT; - surface->gltype = GL_UNSIGNED_BYTE; -- surface->glswapped = true; - break; - case PIXMAN_BE_x8r8g8b8: - case PIXMAN_BE_a8r8g8b8: -@@ -126,7 +125,7 @@ void surface_gl_render_texture(QemuGLShader *gls, - glClearColor(0.1f, 0.1f, 0.1f, 0.0f); - glClear(GL_COLOR_BUFFER_BIT); - -- qemu_gl_run_texture_blit(gls, false, false); -+ qemu_gl_run_texture_blit(gls, false); - } - - void surface_gl_destroy_texture(QemuGLShader *gls, -diff --git a/ui/egl-headless.c b/ui/egl-headless.c -index 5a403d13e6..0f0c3b21f5 100644 ---- a/ui/egl-headless.c -+++ b/ui/egl-headless.c -@@ -161,9 +161,9 @@ static void egl_scanout_flush(DisplayChangeListener *dcl, - if (edpy->cursor_fb.texture) { - /* have cursor -> render using textures */ - egl_texture_blit(edpy->gls, &edpy->blit_fb, &edpy->guest_fb, -- !edpy->y_0_top, false); -+ !edpy->y_0_top); - egl_texture_blend(edpy->gls, &edpy->blit_fb, &edpy->cursor_fb, -- !edpy->y_0_top, false, edpy->pos_x, edpy->pos_y, -+ !edpy->y_0_top, edpy->pos_x, edpy->pos_y, - 1.0, 1.0); - } else { - /* no cursor -> use simple framebuffer blit */ -diff --git a/ui/egl-helpers.c b/ui/egl-helpers.c -index 42d2d511b1..6d0304fa40 100644 ---- a/ui/egl-helpers.c -+++ b/ui/egl-helpers.c -@@ -198,17 +198,17 @@ void egl_fb_read_rect(DisplaySurface *dst, egl_fb *src, int x, int y, int w, int - glPixelStorei(GL_PACK_ROW_LENGTH, 0); - } - --void egl_texture_blit(QemuGLShader *gls, egl_fb *dst, egl_fb *src, bool flip, bool swap) -+void egl_texture_blit(QemuGLShader *gls, egl_fb *dst, egl_fb *src, bool flip) - { - glBindFramebuffer(GL_FRAMEBUFFER_EXT, dst->framebuffer); - glViewport(0, 0, dst->width, dst->height); - glEnable(GL_TEXTURE_2D); - glBindTexture(src->texture_target, src->texture); -- qemu_gl_run_texture_blit(gls, flip, swap); -+ qemu_gl_run_texture_blit(gls, flip); - } - - void egl_texture_blend(QemuGLShader *gls, egl_fb *dst, egl_fb *src, bool flip, -- bool swap, int x, int y, double scale_x, double scale_y) -+ int x, int y, double scale_x, double scale_y) - { - glBindFramebuffer(GL_FRAMEBUFFER_EXT, dst->framebuffer); - int w = scale_x * src->width; -@@ -222,7 +222,7 @@ void egl_texture_blend(QemuGLShader *gls, egl_fb *dst, egl_fb *src, bool flip, - glBindTexture(src->texture_target, src->texture); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); -- qemu_gl_run_texture_blit(gls, flip, swap); -+ qemu_gl_run_texture_blit(gls, flip); - glDisable(GL_BLEND); - } - -diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c -index 2b94079b15..6b6ac1789b 100644 ---- a/ui/gtk-egl.c -+++ b/ui/gtk-egl.c -@@ -355,9 +355,9 @@ void gd_egl_scanout_flush(DisplayChangeListener *dcl, - egl_fb_setup_default(&vc->gfx.win_fb, ww, wh); - if (vc->gfx.cursor_fb.texture) { - egl_texture_blit(vc->gfx.gls, &vc->gfx.win_fb, &vc->gfx.guest_fb, -- vc->gfx.y0_top, false); -+ vc->gfx.y0_top); - egl_texture_blend(vc->gfx.gls, &vc->gfx.win_fb, &vc->gfx.cursor_fb, -- vc->gfx.y0_top, false, -+ vc->gfx.y0_top, - vc->gfx.cursor_x, vc->gfx.cursor_y, - vc->gfx.scale_x, vc->gfx.scale_y); - } else { -diff --git a/ui/shader.c b/ui/shader.c -index a0fb21c0c0..c5c9f754cc 100644 ---- a/ui/shader.c -+++ b/ui/shader.c -@@ -30,13 +30,10 @@ - #include "ui/shader/texture-blit-vert.h" - #include "ui/shader/texture-blit-flip-vert.h" - #include "ui/shader/texture-blit-frag.h" --#include "ui/shader/texture-blit-swap-frag.h" - - struct QemuGLShader { - GLint texture_blit_prog; - GLint texture_blit_flip_prog; -- GLint texture_blit_swap_prog; -- GLint texture_blit_flip_swap_prog; - GLint texture_blit_vao; - }; - -@@ -72,17 +69,11 @@ static GLuint qemu_gl_init_texture_blit(GLint texture_blit_prog) - return vao; - } - --void qemu_gl_run_texture_blit(QemuGLShader *gls, bool flip, bool swapped) -+void qemu_gl_run_texture_blit(QemuGLShader *gls, bool flip) - { -- if (flip && swapped) { -- glUseProgram(gls->texture_blit_flip_swap_prog); -- } else if (flip && !swapped) { -- glUseProgram(gls->texture_blit_flip_prog); -- } else if (!flip && swapped) { -- glUseProgram(gls->texture_blit_swap_prog); -- } else { // !flip && !swapped -- glUseProgram(gls->texture_blit_prog); -- } -+ glUseProgram(flip -+ ? gls->texture_blit_flip_prog -+ : gls->texture_blit_prog); - glBindVertexArray(gls->texture_blit_vao); - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - } -@@ -174,14 +165,7 @@ QemuGLShader *qemu_gl_init_shader(void) - strcpy(vert_src_body, texture_blit_flip_vert_src); - gls->texture_blit_flip_prog = qemu_gl_create_compile_link_program - (vert_src, frag_src); -- strcpy(frag_src_body, texture_blit_swap_frag_src); -- gls->texture_blit_flip_swap_prog = qemu_gl_create_compile_link_program -- (vert_src, frag_src); -- strcpy(vert_src_body, texture_blit_vert_src); -- gls->texture_blit_swap_prog = qemu_gl_create_compile_link_program -- (vert_src, frag_src); -- if (!gls->texture_blit_prog || !gls->texture_blit_flip_prog || -- !gls->texture_blit_swap_prog || !gls->texture_blit_flip_swap_prog) { -+ if (!gls->texture_blit_prog || !gls->texture_blit_flip_prog) { - exit(1); - } - -diff --git a/ui/shader/meson.build b/ui/shader/meson.build -index 14f4699cec..3137e65578 100644 ---- a/ui/shader/meson.build -+++ b/ui/shader/meson.build -@@ -1,6 +1,5 @@ - shaders = [ - ['texture-blit', 'frag'], -- ['texture-blit-swap', 'frag'], - ['texture-blit', 'vert'], - ['texture-blit-flip', 'vert'], - ] -diff --git a/ui/shader/texture-blit-swap.frag b/ui/shader/texture-blit-swap.frag -deleted file mode 100644 -index 8c97bcbac5..0000000000 ---- a/ui/shader/texture-blit-swap.frag -+++ /dev/null -@@ -1,7 +0,0 @@ --uniform sampler2D image; --in mediump vec2 ex_tex_coord; --out mediump vec4 out_frag_color; -- --void main(void) { -- out_frag_color = texture(image, ex_tex_coord).zyxw; --} -diff --git a/ui/spice-display.c b/ui/spice-display.c -index 7faa922092..f56f56dd71 100644 ---- a/ui/spice-display.c -+++ b/ui/spice-display.c -@@ -951,7 +951,7 @@ static int spice_iosurface_create_fd(SimpleSpiceDisplay *ssd, int *fourcc) - return fds[0]; - } - --static void spice_iosurface_blit(SimpleSpiceDisplay *ssd, GLuint src_texture, bool flip, bool swap) -+static void spice_iosurface_blit(SimpleSpiceDisplay *ssd, GLuint src_texture, bool flip) - { - egl_fb tmp_fb = { .texture = src_texture, .texture_target = GL_TEXTURE_2D }; - if (!ssd->iosurface) { -@@ -960,7 +960,7 @@ static void spice_iosurface_blit(SimpleSpiceDisplay *ssd, GLuint src_texture, bo - - #if defined(CONFIG_ANGLE) - eglMakeCurrent(qemu_egl_display, ssd->esurface, ssd->esurface, spice_gl_ctx); -- egl_texture_blit(ssd->gls, &ssd->iosurface_fb, &tmp_fb, flip, swap); -+ egl_texture_blit(ssd->gls, &ssd->iosurface_fb, &tmp_fb, flip); - #endif - } - -@@ -1045,7 +1045,7 @@ static void spice_gl_update(DisplayChangeListener *dcl, - surface_gl_update_texture(ssd->gls, ssd->ds, x, y, w, h); - #if defined(CONFIG_IOSURFACE) - if (!qemu_console_is_gl_blocked(ssd->dcl.con)) { -- spice_iosurface_blit(ssd, ssd->ds->texture, true, ssd->ds->glswapped); -+ spice_iosurface_blit(ssd, ssd->ds->texture, true); - } - #endif - ssd->gl_updates++; -@@ -1319,7 +1319,7 @@ static void qemu_spice_gl_update(DisplayChangeListener *dcl, - ptr_y = ssd->ptr_y; - qemu_mutex_unlock(&ssd->lock); - egl_texture_blit(ssd->gls, &ssd->blit_fb, &ssd->guest_fb, -- !y_0_top, false); -+ !y_0_top); - egl_texture_blend(ssd->gls, &ssd->blit_fb, &ssd->cursor_fb, - !y_0_top, false, ptr_x, ptr_y, 1.0, 1.0); - glFlush(); -@@ -1327,7 +1327,7 @@ static void qemu_spice_gl_update(DisplayChangeListener *dcl, - #elif defined(CONFIG_ANGLE) && defined(CONFIG_IOSURFACE) - GLuint tex_id = ssd->tex_id; - y_0_top = ssd->y_0_top; -- spice_iosurface_blit(ssd, tex_id, !y_0_top, false); -+ spice_iosurface_blit(ssd, tex_id, !y_0_top); - //TODO: cursor stuff - #endif - --- -2.41.0 - -From e9dc30b4c4cdf10d82681a28b4bf2e328624a8b9 Mon Sep 17 00:00:00 2001 -From: osy -Date: Sun, 4 Jan 2026 16:07:59 -0800 -Subject: [PATCH 6/9] Revert "egl-headless: Allow to test with Mesa on macOS" - -This reverts commit edba23a4d49e6c6c7b88e10be5f5f29e5d98f2ee. ---- - include/ui/egl-helpers.h | 2 -- - ui/egl-headless.c | 21 --------------------- - ui/egl-helpers.c | 5 ----- - ui/meson.build | 2 +- - 4 files changed, 1 insertion(+), 29 deletions(-) - -diff --git a/include/ui/egl-helpers.h b/include/ui/egl-helpers.h -index 5dd2a6dac0..a2961d067b 100644 ---- a/include/ui/egl-helpers.h -+++ b/include/ui/egl-helpers.h -@@ -68,8 +68,6 @@ EGLSurface qemu_egl_init_buffer_surface(EGLContext ectx, EGLenum buftype, - EGLClientBuffer buffer, const EGLint *attrib_list); - bool qemu_egl_destroy_surface(EGLSurface surface); - --int qemu_egl_init_dpy_surfaceless(DisplayGLMode mode); -- - #if defined(CONFIG_X11) || defined(CONFIG_GBM) - - int qemu_egl_init_dpy_x11(EGLNativeDisplayType dpy, DisplayGLMode mode); -diff --git a/ui/egl-headless.c b/ui/egl-headless.c -index 0f0c3b21f5..ef5cc02dcc 100644 ---- a/ui/egl-headless.c -+++ b/ui/egl-headless.c -@@ -19,10 +19,6 @@ typedef struct egl_dpy { - uint32_t pos_y; - } egl_dpy; - --#ifndef CONFIG_GBM --static EGLContext ctx; --#endif -- - /* ------------------------------------------------------------------ */ - - static void egl_refresh(DisplayChangeListener *dcl) -@@ -46,12 +42,8 @@ static void egl_gfx_switch(DisplayChangeListener *dcl, - static QEMUGLContext egl_create_context(DisplayGLCtx *dgc, - QEMUGLParams *params) - { --#ifdef CONFIG_GBM - eglMakeCurrent(qemu_egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, - qemu_egl_rn_ctx); --#else -- eglMakeCurrent(qemu_egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, ctx); --#endif - return qemu_egl_create_context(dgc, params); - } - -@@ -221,20 +213,7 @@ static void early_egl_headless_init(DisplayOptions *opts) - mode = opts->gl; - } - --#ifdef CONFIG_GBM - egl_init(opts->u.egl_headless.rendernode, mode, &error_fatal); --#else -- if (qemu_egl_init_dpy_surfaceless(mode)) { -- error_report("egl: display init failed"); -- exit(1); -- } -- -- ctx = qemu_egl_init_ctx(); -- if (!ctx) { -- error_report("egl: egl_init_ctx failed"); -- exit(1); -- } --#endif - } - - static void egl_headless_init(DisplayState *ds, DisplayOptions *opts) -diff --git a/ui/egl-helpers.c b/ui/egl-helpers.c -index 6d0304fa40..6a9c68495b 100644 ---- a/ui/egl-helpers.c -+++ b/ui/egl-helpers.c -@@ -584,11 +584,6 @@ static int qemu_egl_init_dpy(EGLNativeDisplayType dpy, - - #endif - --int qemu_egl_init_dpy_surfaceless(DisplayGLMode mode) --{ -- return qemu_egl_init_dpy(EGL_DEFAULT_DISPLAY, EGL_PLATFORM_SURFACELESS_MESA, mode); --} -- - #if defined(CONFIG_X11) || defined(CONFIG_GBM) - int qemu_egl_init_dpy_x11(EGLNativeDisplayType dpy, DisplayGLMode mode) - { -diff --git a/ui/meson.build b/ui/meson.build -index b2f7541dad..a731a60f3a 100644 ---- a/ui/meson.build -+++ b/ui/meson.build -@@ -69,7 +69,7 @@ endif - if opengl.found() - egl_headless_ss = ss.source_set() - egl_headless_ss.add(when: [opengl, pixman], -- if_true: files('egl-headless.c')) -+ if_true: [files('egl-headless.c'), gbm]) - ui_modules += {'egl-headless' : egl_headless_ss} - endif - --- -2.41.0 - -From 23f2733351953e46875be219b39d024e2cadb675 Mon Sep 17 00:00:00 2001 -From: osy -Date: Sun, 4 Jan 2026 16:28:21 -0800 -Subject: [PATCH 7/9] ui/egl: rename CONFIG_ANGLE to CONFIG_EGL - -Synchronize with @akihikodaki's fork ---- - include/ui/egl-helpers.h | 9 +-- - include/ui/spice-display.h | 2 +- - meson.build | 11 ++- - ui/egl-helpers.c | 149 +++++++++++++++++++------------------ - ui/spice-core.c | 2 +- - ui/spice-display.c | 28 ++++--- - 6 files changed, 101 insertions(+), 100 deletions(-) - -diff --git a/include/ui/egl-helpers.h b/include/ui/egl-helpers.h -index a2961d067b..ecf814bec3 100644 ---- a/include/ui/egl-helpers.h -+++ b/include/ui/egl-helpers.h -@@ -6,9 +6,6 @@ - #ifdef CONFIG_GBM - #include - #endif --#ifdef CONFIG_ANGLE --#include --#endif - #include "ui/console.h" - #include "ui/shader.h" - -@@ -68,6 +65,8 @@ EGLSurface qemu_egl_init_buffer_surface(EGLContext ectx, EGLenum buftype, - EGLClientBuffer buffer, const EGLint *attrib_list); - bool qemu_egl_destroy_surface(EGLSurface surface); - -+int qemu_egl_init_dpy_cocoa(DisplayGLMode mode); -+ - #if defined(CONFIG_X11) || defined(CONFIG_GBM) - - int qemu_egl_init_dpy_x11(EGLNativeDisplayType dpy, DisplayGLMode mode); -@@ -79,10 +78,6 @@ int qemu_egl_init_dpy_mesa(EGLNativeDisplayType dpy, DisplayGLMode mode); - int qemu_egl_init_dpy_win32(EGLNativeDisplayType dpy, DisplayGLMode mode); - #endif - --#if defined(CONFIG_ANGLE) --int qemu_egl_init_dpy_angle(DisplayGLMode mode); --#endif -- - EGLContext qemu_egl_init_ctx(void); - bool qemu_egl_has_dmabuf(void); - -diff --git a/include/ui/spice-display.h b/include/ui/spice-display.h -index 4b9121bfb5..5a0b5a0c8f 100644 ---- a/include/ui/spice-display.h -+++ b/include/ui/spice-display.h -@@ -138,7 +138,7 @@ struct SimpleSpiceDisplay { - IOSurfaceRef iosurface; - int surface_send_fd; - #endif --#if defined(CONFIG_ANGLE) -+#if defined(CONFIG_EGL) - EGLSurface esurface; - egl_fb iosurface_fb; - GLuint tex_id; -diff --git a/meson.build b/meson.build -index abe0dcf01c..fff146e5c2 100644 ---- a/meson.build -+++ b/meson.build -@@ -1784,14 +1784,13 @@ if not get_option('coreaudio').auto() or (host_os == 'darwin' and have_system) - required: get_option('coreaudio')) - endif - -+egl = not_found - opengl = not_found - if not get_option('opengl').auto() or have_system or have_vhost_user_gpu -- epoxy = dependency('epoxy', method: 'pkg-config', -+ opengl = dependency('epoxy', method: 'pkg-config', - required: get_option('opengl')) -- if cc.has_header('epoxy/egl.h', dependencies: epoxy) -- opengl = epoxy -- elif get_option('opengl').enabled() -- error('epoxy/egl.h not found') -+ if cc.has_header('epoxy/egl.h', dependencies: opengl) -+ egl = opengl - endif - endif - gbm = not_found -@@ -2538,6 +2537,7 @@ if numa.found() - cc.has_function('numa_has_preferred_many', - dependencies: numa)) - endif -+config_host_data.set('CONFIG_EGL', egl.found()) - config_host_data.set('CONFIG_OPENGL', opengl.found()) - config_host_data.set('CONFIG_PLUGIN', get_option('plugins')) - config_host_data.set('CONFIG_RBD', rbd.found()) -@@ -2661,7 +2661,6 @@ config_host_data.set('HAVE_PTY_H', cc.has_header('pty.h')) - config_host_data.set('HAVE_SYS_DISK_H', cc.has_header('sys/disk.h')) - config_host_data.set('HAVE_SYS_IOCCOM_H', cc.has_header('sys/ioccom.h')) - config_host_data.set('HAVE_SYS_KCOV_H', cc.has_header('sys/kcov.h')) --config_host_data.set('CONFIG_ANGLE', cc.has_header('EGL/eglext_angle.h')) - if host_os == 'windows' - config_host_data.set('HAVE_AFUNIX_H', cc.has_header('afunix.h')) - endif -diff --git a/ui/egl-helpers.c b/ui/egl-helpers.c -index 6a9c68495b..f35223592e 100644 ---- a/ui/egl-helpers.c -+++ b/ui/egl-helpers.c -@@ -472,9 +472,73 @@ bool qemu_egl_destroy_surface(EGLSurface surface) - return eglDestroySurface(qemu_egl_display, surface); - } - -+static int qemu_egl_init_dpy(EGLDisplay dpy, DisplayGLMode mode) -+{ -+ static const EGLint conf_att_core[] = { -+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT, -+ EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, -+ EGL_RED_SIZE, 5, -+ EGL_GREEN_SIZE, 5, -+ EGL_BLUE_SIZE, 5, -+ EGL_ALPHA_SIZE, 0, -+ EGL_NONE, -+ }; -+ static const EGLint conf_att_gles[] = { -+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT, -+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, -+ EGL_RED_SIZE, 5, -+ EGL_GREEN_SIZE, 5, -+ EGL_BLUE_SIZE, 5, -+ EGL_ALPHA_SIZE, 0, -+ EGL_NONE, -+ }; -+ EGLint major, minor; -+ EGLBoolean b; -+ EGLint n; -+ bool gles = (mode == DISPLAY_GL_MODE_ES); -+ -+ qemu_egl_display = dpy; -+ -+ b = eglInitialize(qemu_egl_display, &major, &minor); -+ if (b == EGL_FALSE) { -+ error_report("egl: eglInitialize failed"); -+ return -1; -+ } -+ -+ b = eglBindAPI(gles ? EGL_OPENGL_ES_API : EGL_OPENGL_API); -+ if (b == EGL_FALSE) { -+ error_report("egl: eglBindAPI failed (%s mode)", -+ gles ? "gles" : "core"); -+ return -1; -+ } -+ -+ b = eglChooseConfig(qemu_egl_display, -+ gles ? conf_att_gles : conf_att_core, -+ &qemu_egl_config, 1, &n); -+ if (b == EGL_FALSE || n != 1) { -+ error_report("egl: eglChooseConfig failed (%s mode)", -+ gles ? "gles" : "core"); -+ return -1; -+ } -+ -+ qemu_egl_mode = gles ? DISPLAY_GL_MODE_ES : DISPLAY_GL_MODE_CORE; -+ return 0; -+} -+ -+int qemu_egl_init_dpy_cocoa(DisplayGLMode mode) -+{ -+ EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); -+ if (dpy == EGL_NO_DISPLAY) { -+ error_report("egl: eglGetDisplay failed"); -+ return -1; -+ } -+ -+ return qemu_egl_init_dpy(dpy, mode); -+} -+ - /* ---------------------------------------------------------------------- */ - --#if defined(CONFIG_X11) || defined(CONFIG_GBM) || defined(CONFIG_ANGLE) || defined(WIN32) -+#if defined(CONFIG_X11) || defined(CONFIG_GBM) || defined(WIN32) - - /* - * Taken from glamor_egl.h from the Xorg xserver, which is MIT licensed -@@ -504,15 +568,16 @@ bool qemu_egl_destroy_surface(EGLSurface surface) - * platform extensions (EGL_KHR_platform_gbm and friends) yet it doesn't seem - * like mesa will be able to advertise these (even though it can do EGL 1.5). - */ --static EGLDisplay qemu_egl_get_display(EGLNativeDisplayType native, -- EGLenum platform) -+static int qemu_egl_init_dpy_platform(EGLNativeDisplayType native, -+ EGLenum platform, -+ DisplayGLMode mode) - { - EGLDisplay dpy = EGL_NO_DISPLAY; - - /* In practise any EGL 1.5 implementation would support the EXT extension */ - if (epoxy_has_egl_extension(NULL, "EGL_EXT_platform_base")) { - if (platform != 0) { -- dpy = eglGetPlatformDisplayEXT(platform, (void *)native, NULL); -+ dpy = eglGetPlatformDisplayEXT(platform, native, NULL); - } - } - -@@ -520,66 +585,13 @@ static EGLDisplay qemu_egl_get_display(EGLNativeDisplayType native, - /* fallback */ - dpy = eglGetDisplay(native); - } -- return dpy; --} -- --static int qemu_egl_init_dpy(EGLNativeDisplayType dpy, -- EGLenum platform, -- DisplayGLMode mode) --{ -- static const EGLint conf_att_core[] = { -- EGL_SURFACE_TYPE, EGL_WINDOW_BIT, -- EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, -- EGL_RED_SIZE, 5, -- EGL_GREEN_SIZE, 5, -- EGL_BLUE_SIZE, 5, -- EGL_ALPHA_SIZE, 0, -- EGL_NONE, -- }; -- static const EGLint conf_att_gles[] = { -- EGL_SURFACE_TYPE, EGL_WINDOW_BIT, -- EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, -- EGL_RED_SIZE, 5, -- EGL_GREEN_SIZE, 5, -- EGL_BLUE_SIZE, 5, -- EGL_ALPHA_SIZE, 0, -- EGL_NONE, -- }; -- EGLint major, minor; -- EGLBoolean b; -- EGLint n; -- bool gles = (mode == DISPLAY_GL_MODE_ES); -- -- qemu_egl_display = qemu_egl_get_display(dpy, platform); -- if (qemu_egl_display == EGL_NO_DISPLAY) { -- error_report("egl: eglGetDisplay failed: %s", qemu_egl_get_error_string()); -- return -1; -- } -- -- b = eglInitialize(qemu_egl_display, &major, &minor); -- if (b == EGL_FALSE) { -- error_report("egl: eglInitialize failed: %s", qemu_egl_get_error_string()); -- return -1; -- } -- -- b = eglBindAPI(gles ? EGL_OPENGL_ES_API : EGL_OPENGL_API); -- if (b == EGL_FALSE) { -- error_report("egl: eglBindAPI failed (%s mode): %s", -- gles ? "gles" : "core", qemu_egl_get_error_string()); -- return -1; -- } - -- b = eglChooseConfig(qemu_egl_display, -- gles ? conf_att_gles : conf_att_core, -- &qemu_egl_config, 1, &n); -- if (b == EGL_FALSE || n != 1) { -- error_report("egl: eglChooseConfig failed (%s mode): %s", -- gles ? "gles" : "core", qemu_egl_get_error_string()); -+ if (dpy == EGL_NO_DISPLAY) { -+ error_report("egl: eglGetDisplay failed"); - return -1; - } - -- qemu_egl_mode = gles ? DISPLAY_GL_MODE_ES : DISPLAY_GL_MODE_CORE; -- return 0; -+ return qemu_egl_init_dpy(dpy, mode); - } - - #endif -@@ -588,18 +600,18 @@ static int qemu_egl_init_dpy(EGLNativeDisplayType dpy, - int qemu_egl_init_dpy_x11(EGLNativeDisplayType dpy, DisplayGLMode mode) - { - #ifdef EGL_KHR_platform_x11 -- return qemu_egl_init_dpy(dpy, EGL_PLATFORM_X11_KHR, mode); -+ return qemu_egl_init_dpy_platform(dpy, EGL_PLATFORM_X11_KHR, mode); - #else -- return qemu_egl_init_dpy(dpy, 0, mode); -+ return qemu_egl_init_dpy_platform(dpy, 0, mode); - #endif - } - - int qemu_egl_init_dpy_mesa(EGLNativeDisplayType dpy, DisplayGLMode mode) - { - #ifdef EGL_MESA_platform_gbm -- return qemu_egl_init_dpy(dpy, EGL_PLATFORM_GBM_MESA, mode); -+ return qemu_egl_init_dpy_platform(dpy, EGL_PLATFORM_GBM_MESA, mode); - #else -- return qemu_egl_init_dpy(dpy, 0, mode); -+ return qemu_egl_init_dpy_platform(dpy, 0, mode); - #endif - } - #endif -@@ -643,15 +655,6 @@ int qemu_egl_init_dpy_win32(EGLNativeDisplayType dpy, DisplayGLMode mode) - } - #endif - --#if defined(CONFIG_ANGLE) -- --int qemu_egl_init_dpy_angle(DisplayGLMode mode) --{ -- return qemu_egl_init_dpy(EGL_DEFAULT_DISPLAY, EGL_PLATFORM_ANGLE_ANGLE, mode); --} -- --#endif -- - bool qemu_egl_has_dmabuf(void) - { - if (qemu_egl_display == EGL_NO_DISPLAY) { -diff --git a/ui/spice-core.c b/ui/spice-core.c -index 43715cb60d..5b0fa38ecf 100644 ---- a/ui/spice-core.c -+++ b/ui/spice-core.c -@@ -52,7 +52,7 @@ static int spice_have_target_host; - - static QemuThread me; - --#ifdef CONFIG_ANGLE -+#ifdef CONFIG_EGL - extern EGLContext spice_gl_ctx; - #endif - -diff --git a/ui/spice-display.c b/ui/spice-display.c -index f56f56dd71..67aaf9ae89 100644 ---- a/ui/spice-display.c -+++ b/ui/spice-display.c -@@ -29,7 +29,7 @@ - #ifdef CONFIG_IOSURFACE - #include - #endif --#ifdef CONFIG_ANGLE -+#ifdef CONFIG_EGL - #include - #include - #endif -@@ -38,7 +38,7 @@ - - bool spice_opengl; - --#ifdef CONFIG_ANGLE -+#ifdef CONFIG_EGL - EGLContext spice_gl_ctx; - #endif - -@@ -806,6 +806,10 @@ static const DisplayChangeListenerOps display_listener_ops = { - - #if defined(CONFIG_IOSURFACE) - -+#ifndef EGL_IOSURFACE_WRITE_HINT_ANGLE -+#define EGL_IOSURFACE_WRITE_HINT_ANGLE (0x0002) -+#endif -+ - static void AddIntegerValue(CFMutableDictionaryRef dictionary, const CFStringRef key, int32_t value) - { - CFNumberRef number = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &value); -@@ -833,7 +837,7 @@ static int spice_iosurface_create(SimpleSpiceDisplay *ssd, int width, int height - return 0; - } - --#if defined(CONFIG_ANGLE) -+#if defined(CONFIG_EGL) - EGLint target = 0; - GLenum tex_target = 0; - if (eglGetConfigAttrib(qemu_egl_display, -@@ -892,7 +896,7 @@ static void spice_iosurface_destroy(SimpleSpiceDisplay *ssd) - if (!ssd->iosurface) { - return; - } --#if defined(CONFIG_ANGLE) -+#if defined(CONFIG_EGL) - eglMakeCurrent(qemu_egl_display, ssd->esurface, ssd->esurface, spice_gl_ctx); - eglReleaseTexImage(qemu_egl_display, ssd->esurface, EGL_BACK_BUFFER); - egl_fb_destroy(&ssd->iosurface_fb); -@@ -958,7 +962,7 @@ static void spice_iosurface_blit(SimpleSpiceDisplay *ssd, GLuint src_texture, bo - return; - } - --#if defined(CONFIG_ANGLE) -+#if defined(CONFIG_EGL) - eglMakeCurrent(qemu_egl_display, ssd->esurface, ssd->esurface, spice_gl_ctx); - egl_texture_blit(ssd->gls, &ssd->iosurface_fb, &tmp_fb, flip); - #endif -@@ -1113,7 +1117,7 @@ static QEMUGLContext qemu_spice_gl_create_context(DisplayGLCtx *dgc, - #if defined(CONFIG_GBM) - eglMakeCurrent(qemu_egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, - qemu_egl_rn_ctx); --#elif defined(CONFIG_ANGLE) -+#elif defined(CONFIG_EGL) - eglMakeCurrent(qemu_egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, - spice_gl_ctx); - #endif -@@ -1132,7 +1136,7 @@ static void qemu_spice_gl_scanout_disable(DisplayChangeListener *dcl) - #if defined(CONFIG_IOSURFACE) - spice_iosurface_destroy(ssd); - #endif --#if defined(CONFIG_ANGLE) -+#if defined(CONFIG_EGL) - ssd->tex_id = -1; - #endif - } -@@ -1154,7 +1158,7 @@ static void qemu_spice_gl_scanout_texture(DisplayChangeListener *dcl, - fd = egl_get_fd_for_texture(tex_id, &stride, &fourcc, NULL); - #elif defined(CONFIG_IOSURFACE) - if (spice_iosurface_resize(ssd, backing_width, backing_height)) { --#if defined(CONFIG_ANGLE) -+#if defined(CONFIG_EGL) - ssd->tex_id = tex_id; - ssd->y_0_top = y_0_top; - #endif -@@ -1324,7 +1328,7 @@ static void qemu_spice_gl_update(DisplayChangeListener *dcl, - !y_0_top, false, ptr_x, ptr_y, 1.0, 1.0); - glFlush(); - } --#elif defined(CONFIG_ANGLE) && defined(CONFIG_IOSURFACE) -+#elif defined(CONFIG_EGL) && defined(CONFIG_IOSURFACE) - GLuint tex_id = ssd->tex_id; - y_0_top = ssd->y_0_top; - spice_iosurface_blit(ssd, tex_id, !y_0_top); -@@ -1395,7 +1399,7 @@ static void qemu_spice_display_init_one(QemuConsole *con) - ssd->iosurface = NULL; - ssd->surface_send_fd = -1; - #endif --#if defined(CONFIG_ANGLE) -+#if defined(CONFIG_EGL) - ssd->esurface = EGL_NO_SURFACE; - ssd->tex_id = -1; - #endif -@@ -1444,8 +1448,8 @@ void qemu_spice_display_early_init(void) - } - #if defined(CONFIG_GBM) - egl_init(qemu_opt_get(opts, "rendernode"), DISPLAY_GL_MODE_ON, &error_fatal); --#elif defined(CONFIG_ANGLE) -- if (qemu_egl_init_dpy_angle(DISPLAY_GL_MODE_ES)) { -+#elif defined(CONFIG_EGL) -+ if (qemu_egl_init_dpy_cocoa(DISPLAY_GL_MODE_ES)) { - error_report("SPICE GL failed to initialize ANGLE display"); - exit(1); - } --- -2.41.0 - -From 6d6bc66ecf1e8e409b780aaab4988ac8ebcd1baf Mon Sep 17 00:00:00 2001 -From: osy -Date: Sun, 4 Jan 2026 17:03:42 -0800 -Subject: [PATCH 8/9] ui/spice-display: refactor IOSurface code for EGL - ---- - ui/spice-display.c | 102 ++++++++++++++++++++++++++++----------------- - 1 file changed, 63 insertions(+), 39 deletions(-) - -diff --git a/ui/spice-display.c b/ui/spice-display.c -index 67aaf9ae89..931258ef96 100644 ---- a/ui/spice-display.c -+++ b/ui/spice-display.c -@@ -817,26 +817,9 @@ static void AddIntegerValue(CFMutableDictionaryRef dictionary, const CFStringRef - CFRelease(number); - } - --static int spice_iosurface_create(SimpleSpiceDisplay *ssd, int width, int height) -+static bool spice_iosurface_create_egl(SimpleSpiceDisplay *ssd, int width, int height, -+ IOSurfaceRef surface) - { -- CFMutableDictionaryRef dict = CFDictionaryCreateMutable( -- kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); -- AddIntegerValue(dict, kIOSurfaceWidth, width); -- AddIntegerValue(dict, kIOSurfaceHeight, height); -- AddIntegerValue(dict, kIOSurfacePixelFormat, 'BGRA'); -- AddIntegerValue(dict, kIOSurfaceBytesPerElement, 4); --#if TARGET_OS_OSX -- CFDictionaryAddValue(dict, kIOSurfaceIsGlobal, kCFBooleanTrue); --#endif -- -- ssd->iosurface = IOSurfaceCreate(dict); -- CFRelease(dict); -- -- if (!ssd->iosurface) { -- error_report("spice_iosurface_create: IOSurfaceCreate failed"); -- return 0; -- } -- - #if defined(CONFIG_EGL) - EGLint target = 0; - GLenum tex_target = 0; -@@ -845,7 +828,7 @@ static int spice_iosurface_create(SimpleSpiceDisplay *ssd, int width, int height - EGL_BIND_TO_TEXTURE_TARGET_ANGLE, - &target) != EGL_TRUE) { - error_report("spice_iosurface_create: eglGetConfigAttrib failed"); -- goto gl_error; -+ return false; - } - if (target == EGL_TEXTURE_2D) { - tex_target = GL_TEXTURE_2D; -@@ -853,7 +836,7 @@ static int spice_iosurface_create(SimpleSpiceDisplay *ssd, int width, int height - tex_target = GL_TEXTURE_RECTANGLE_ANGLE; - } else { - error_report("spice_iosurface_create: unsupported texture target"); -- goto gl_error; -+ return false; - } - - const EGLint attribs[] = { -@@ -869,33 +852,57 @@ static int spice_iosurface_create(SimpleSpiceDisplay *ssd, int width, int height - }; - ssd->esurface = qemu_egl_init_buffer_surface(spice_gl_ctx, - EGL_IOSURFACE_ANGLE, -- ssd->iosurface, -+ surface, - attribs); - - if (ssd->esurface == NULL) { -- goto gl_error; -+ return false; - } - - egl_fb_setup_new_tex_target(&ssd->iosurface_fb, width, height, tex_target); - - eglBindTexImage(qemu_egl_display, ssd->esurface, EGL_BACK_BUFFER); - -- return 1; --gl_error: -- CFRelease(ssd->iosurface); -- ssd->iosurface = NULL; -- return 0; -+ return true; - #else - error_report("spice_iosurface_create: ANGLE not found"); -- return 0; -+ return false; - #endif - } - --static void spice_iosurface_destroy(SimpleSpiceDisplay *ssd) -+static bool spice_iosurface_create(SimpleSpiceDisplay *ssd, int width, int height) - { -- if (!ssd->iosurface) { -- return; -+ IOSurfaceRef surface; -+ CFMutableDictionaryRef dict = CFDictionaryCreateMutable( -+ kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); -+ AddIntegerValue(dict, kIOSurfaceWidth, width); -+ AddIntegerValue(dict, kIOSurfaceHeight, height); -+ AddIntegerValue(dict, kIOSurfacePixelFormat, 'BGRA'); -+ AddIntegerValue(dict, kIOSurfaceBytesPerElement, 4); -+#if TARGET_OS_OSX -+ CFDictionaryAddValue(dict, kIOSurfaceIsGlobal, kCFBooleanTrue); -+#endif -+ -+ surface = IOSurfaceCreate(dict); -+ CFRelease(dict); -+ -+ if (!surface) { -+ error_report("spice_iosurface_create: IOSurfaceCreate failed"); -+ return false; -+ } -+ -+ if (!spice_iosurface_create_egl(ssd, width, height, surface)) { -+ CFRelease(surface); -+ return false; - } -+ -+ ssd->iosurface = surface; -+ -+ return true; -+} -+ -+static void spice_iosurface_destroy_egl(SimpleSpiceDisplay *ssd) -+{ - #if defined(CONFIG_EGL) - eglMakeCurrent(qemu_egl_display, ssd->esurface, ssd->esurface, spice_gl_ctx); - eglReleaseTexImage(qemu_egl_display, ssd->esurface, EGL_BACK_BUFFER); -@@ -903,6 +910,16 @@ static void spice_iosurface_destroy(SimpleSpiceDisplay *ssd) - qemu_egl_destroy_surface(ssd->esurface); - ssd->esurface = EGL_NO_SURFACE; - #endif -+} -+ -+static void spice_iosurface_destroy(SimpleSpiceDisplay *ssd) -+{ -+ if (!ssd->iosurface) { -+ return; -+ } -+ -+ spice_iosurface_destroy_egl(ssd); -+ - if (ssd->surface_send_fd > -1) { - // this sends POLLHUP and indicates that any unread data is stale - // and should not be used -@@ -914,7 +931,7 @@ static void spice_iosurface_destroy(SimpleSpiceDisplay *ssd) - ssd->iosurface = NULL; - } - --static int spice_iosurface_resize(SimpleSpiceDisplay *ssd, int width, int height) -+static bool spice_iosurface_resize(SimpleSpiceDisplay *ssd, int width, int height) - { - if (ssd->iosurface) { - if (IOSurfaceGetHeight(ssd->iosurface) != height || -@@ -922,7 +939,7 @@ static int spice_iosurface_resize(SimpleSpiceDisplay *ssd, int width, int height - spice_iosurface_destroy(ssd); - return spice_iosurface_create(ssd, width, height); - } else { -- return 1; -+ return true; - } - } else { - return spice_iosurface_create(ssd, width, height); -@@ -955,19 +972,26 @@ static int spice_iosurface_create_fd(SimpleSpiceDisplay *ssd, int *fourcc) - return fds[0]; - } - --static void spice_iosurface_blit(SimpleSpiceDisplay *ssd, GLuint src_texture, bool flip) -+static void spice_iosurface_blit_egl(SimpleSpiceDisplay *ssd, GLuint src_texture, -+ bool flip) - { -+#if defined(CONFIG_EGL) - egl_fb tmp_fb = { .texture = src_texture, .texture_target = GL_TEXTURE_2D }; -- if (!ssd->iosurface) { -- return; -- } - --#if defined(CONFIG_EGL) - eglMakeCurrent(qemu_egl_display, ssd->esurface, ssd->esurface, spice_gl_ctx); - egl_texture_blit(ssd->gls, &ssd->iosurface_fb, &tmp_fb, flip); - #endif - } - -+static void spice_iosurface_blit(SimpleSpiceDisplay *ssd, GLuint src_texture, bool flip) -+{ -+ if (!ssd->iosurface) { -+ return; -+ } -+ -+ spice_iosurface_blit_egl(ssd, src_texture, flip); -+} -+ - #endif - - static void qemu_spice_gl_monitor_config(SimpleSpiceDisplay *ssd, --- -2.41.0 - -From 47f3d4eb42d9fa8d035fb67965840a16fcdc018d Mon Sep 17 00:00:00 2001 -From: osy -Date: Sun, 4 Jan 2026 20:22:19 -0800 -Subject: [PATCH 9/9] ui/spice-display: support rendering with CGL - ---- - include/ui/spice-display.h | 4 +- - meson.build | 2 + - ui/meson.build | 3 + - ui/spice-core.c | 2 +- - ui/spice-display.c | 231 ++++++++++++++++++++++++++++++------- - 5 files changed, 199 insertions(+), 43 deletions(-) - -diff --git a/include/ui/spice-display.h b/include/ui/spice-display.h -index 5a0b5a0c8f..ec55194139 100644 ---- a/include/ui/spice-display.h -+++ b/include/ui/spice-display.h -@@ -141,9 +141,9 @@ struct SimpleSpiceDisplay { - #if defined(CONFIG_EGL) - EGLSurface esurface; - egl_fb iosurface_fb; -+#endif - GLuint tex_id; - bool y_0_top; --#endif - bool render_cursor; - - egl_fb guest_fb; -@@ -167,8 +167,6 @@ struct SimpleSpiceCursor { - QXLCursor cursor; - }; - --extern bool spice_opengl; -- - int qemu_spice_rect_is_empty(const QXLRect* r); - void qemu_spice_rect_union(QXLRect *dest, const QXLRect *r); - -diff --git a/meson.build b/meson.build -index fff146e5c2..af9284fb29 100644 ---- a/meson.build -+++ b/meson.build -@@ -836,6 +836,7 @@ coref = [] - iokit = [] - iosurface = not_found - pvg = not_found -+quartzcore = not_found - emulator_link_args = [] - midl = not_found - widl = not_found -@@ -860,6 +861,7 @@ elif host_os == 'darwin' - host_dsosuf = '.dylib' - pvg = dependency('appleframeworks', modules: ['ParavirtualizedGraphics', 'Metal'], - required: get_option('pvg')) -+ quartzcore = dependency('appleframeworks', modules: ['OpenGL', 'QuartzCore'], required: false) - elif host_os == 'sunos' - socket = [cc.find_library('socket'), - cc.find_library('nsl'), -diff --git a/ui/meson.build b/ui/meson.build -index a731a60f3a..8acb9c0ef0 100644 ---- a/ui/meson.build -+++ b/ui/meson.build -@@ -141,6 +141,9 @@ if spice.found() - if iosurface.found() - spice_core_ss.add(iosurface) - endif -+ if quartzcore.found() -+ spice_core_ss.add(quartzcore) -+ endif - ui_modules += {'spice-core' : spice_core_ss} - - if gio.found() -diff --git a/ui/spice-core.c b/ui/spice-core.c -index 5b0fa38ecf..91817b1239 100644 ---- a/ui/spice-core.c -+++ b/ui/spice-core.c -@@ -510,7 +510,7 @@ static QemuOptsList qemu_spice_opts = { - #ifdef HAVE_SPICE_GL - },{ - .name = "gl", -- .type = QEMU_OPT_BOOL, -+ .type = QEMU_OPT_STRING, - },{ - .name = "rendernode", - .type = QEMU_OPT_STRING, -diff --git a/ui/spice-display.c b/ui/spice-display.c -index 931258ef96..97a915a7ee 100644 ---- a/ui/spice-display.c -+++ b/ui/spice-display.c -@@ -28,6 +28,10 @@ - #include "trace.h" - #ifdef CONFIG_IOSURFACE - #include -+#if TARGET_OS_OSX -+#import -+#define HAVE_SPICE_MAC_CGL -+#endif - #endif - #ifdef CONFIG_EGL - #include -@@ -36,11 +40,8 @@ - - #include "ui/spice-display.h" - --bool spice_opengl; -- --#ifdef CONFIG_EGL --EGLContext spice_gl_ctx; --#endif -+static DisplayGLMode spice_opengl; -+QEMUGLContext spice_gl_ctx; - - int qemu_spice_rect_is_empty(const QXLRect* r) - { -@@ -870,6 +871,39 @@ static bool spice_iosurface_create_egl(SimpleSpiceDisplay *ssd, int width, int h - #endif - } - -+static bool spice_iosurface_create_cgl(SimpleSpiceDisplay *ssd, int width, int height, -+ IOSurfaceRef surface) -+{ -+#if defined(HAVE_SPICE_MAC_CGL) -+ GLuint tex; -+ -+ CGLSetCurrentContext(spice_gl_ctx); -+ glGenTextures(1, &tex); -+ glBindTexture(GL_TEXTURE_RECTANGLE, tex); -+ if (CGLTexImageIOSurface2D(spice_gl_ctx, -+ GL_TEXTURE_RECTANGLE, -+ GL_RGBA, -+ width, -+ height, -+ GL_BGRA, -+ GL_UNSIGNED_INT_8_8_8_8_REV, -+ surface, -+ 0) != kCGLNoError) { -+ glDeleteTextures(1, &tex); -+ error_report("spice_iosurface_create: failed to create CGL texture"); -+ return false; -+ } -+ -+ egl_fb_setup_for_tex_target(&ssd->iosurface_fb, width, height, -+ tex, GL_TEXTURE_RECTANGLE, true); -+ -+ return true; -+#else -+ error_report("spice_iosurface_create: CGL not found"); -+ return false; -+#endif -+} -+ - static bool spice_iosurface_create(SimpleSpiceDisplay *ssd, int width, int height) - { - IOSurfaceRef surface; -@@ -891,9 +925,16 @@ static bool spice_iosurface_create(SimpleSpiceDisplay *ssd, int width, int heigh - return false; - } - -- if (!spice_iosurface_create_egl(ssd, width, height, surface)) { -- CFRelease(surface); -- return false; -+ if (spice_opengl == DISPLAY_GL_MODE_CORE) { -+ if (!spice_iosurface_create_cgl(ssd, width, height, surface)) { -+ CFRelease(surface); -+ return false; -+ } -+ } else { -+ if (!spice_iosurface_create_egl(ssd, width, height, surface)) { -+ CFRelease(surface); -+ return false; -+ } - } - - ssd->iosurface = surface; -@@ -912,13 +953,25 @@ static void spice_iosurface_destroy_egl(SimpleSpiceDisplay *ssd) - #endif - } - -+static void spice_iosurface_destroy_cgl(SimpleSpiceDisplay *ssd) -+{ -+#if defined(HAVE_SPICE_MAC_CGL) -+ CGLSetCurrentContext(spice_gl_ctx); -+ egl_fb_destroy(&ssd->iosurface_fb); -+#endif -+} -+ - static void spice_iosurface_destroy(SimpleSpiceDisplay *ssd) - { - if (!ssd->iosurface) { - return; - } - -- spice_iosurface_destroy_egl(ssd); -+ if (spice_opengl == DISPLAY_GL_MODE_CORE) { -+ spice_iosurface_destroy_cgl(ssd); -+ } else { -+ spice_iosurface_destroy_egl(ssd); -+ } - - if (ssd->surface_send_fd > -1) { - // this sends POLLHUP and indicates that any unread data is stale -@@ -983,15 +1036,67 @@ static void spice_iosurface_blit_egl(SimpleSpiceDisplay *ssd, GLuint src_texture - #endif - } - -+static void spice_iosurface_blit_cgl(SimpleSpiceDisplay *ssd, GLuint src_texture, -+ bool flip) -+{ -+#if defined(HAVE_SPICE_MAC_CGL) -+ egl_fb tmp_fb = { .texture = src_texture, .texture_target = GL_TEXTURE_2D }; -+ -+ CGLSetCurrentContext(spice_gl_ctx); -+ egl_texture_blit(ssd->gls, &ssd->iosurface_fb, &tmp_fb, flip); -+#endif -+} -+ - static void spice_iosurface_blit(SimpleSpiceDisplay *ssd, GLuint src_texture, bool flip) - { - if (!ssd->iosurface) { - return; - } - -- spice_iosurface_blit_egl(ssd, src_texture, flip); -+ if (spice_opengl == DISPLAY_GL_MODE_CORE) { -+ spice_iosurface_blit_cgl(ssd, src_texture, flip); -+ } else { -+ spice_iosurface_blit_egl(ssd, src_texture, flip); -+ } -+} -+ -+#endif -+ -+#ifdef HAVE_SPICE_MAC_CGL -+static CGLContextObj spice_cgl_create_context(CGLContextObj share) -+{ -+ CGLPixelFormatAttribute attrs[] = { -+ kCGLPFAOpenGLProfile, -+ /* Request 3.2 core; macOS delivers 4.1 for this selector. */ -+ (CGLPixelFormatAttribute)kCGLOGLPVersion_3_2_Core, -+ kCGLPFAColorSize, 32, -+ kCGLPFADoubleBuffer, -+ 0 -+ }; -+ -+ CGLPixelFormatObj pix; -+ GLint npix; -+ CGLChoosePixelFormat(attrs, &pix, &npix); -+ -+ CGLContextObj ctx; -+ CGLCreateContext(pix, share, &ctx); -+ CGLReleasePixelFormat(pix); -+ -+ return ctx; - } - -+static void spice_cgl_destroy_context(CGLContextObj ctx) -+{ -+ if (CGLGetCurrentContext() == ctx) { -+ CGLSetCurrentContext(NULL); -+ } -+ CGLReleaseContext(ctx); -+} -+ -+static int spice_cgl_make_context_current(CGLContextObj ctx) -+{ -+ return CGLSetCurrentContext(ctx); -+} - #endif - - static void qemu_spice_gl_monitor_config(SimpleSpiceDisplay *ssd, -@@ -1043,6 +1148,9 @@ static void qemu_spice_gl_block_timer(void *opaque) - warn_report("spice: no gl-draw-done within one second"); - } - -+static int qemu_spice_gl_make_context_current(DisplayGLCtx *dgc, -+ QEMUGLContext ctx); -+ - static void spice_gl_refresh(DisplayChangeListener *dcl) - { - SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); -@@ -1052,6 +1160,7 @@ static void spice_gl_refresh(DisplayChangeListener *dcl) - return; - } - -+ qemu_spice_gl_make_context_current(NULL, spice_gl_ctx); - graphic_hw_update(dcl->con); - if (ssd->gl_updates && ssd->have_surface) { - qemu_spice_gl_block(ssd, true); -@@ -1070,6 +1179,7 @@ static void spice_gl_update(DisplayChangeListener *dcl, - { - SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); - -+ qemu_spice_gl_make_context_current(NULL, spice_gl_ctx); - surface_gl_update_texture(ssd->gls, ssd->ds, x, y, w, h); - #if defined(CONFIG_IOSURFACE) - if (!qemu_console_is_gl_blocked(ssd->dcl.con)) { -@@ -1087,6 +1197,7 @@ static void spice_gl_switch(DisplayChangeListener *dcl, - int fd = -1; - int width = 0, height = 0; - -+ qemu_spice_gl_make_context_current(NULL, spice_gl_ctx); - if (ssd->ds) { - surface_gl_destroy_texture(ssd->gls, ssd->ds); - } -@@ -1138,14 +1249,43 @@ static void spice_gl_switch(DisplayChangeListener *dcl, - static QEMUGLContext qemu_spice_gl_create_context(DisplayGLCtx *dgc, - QEMUGLParams *params) - { -+ if (spice_opengl == DISPLAY_GL_MODE_CORE) { -+#if defined(HAVE_SPICE_MAC_CGL) -+ return spice_cgl_create_context(spice_gl_ctx); -+#endif -+ } else { - #if defined(CONFIG_GBM) -- eglMakeCurrent(qemu_egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, -- qemu_egl_rn_ctx); -+ eglMakeCurrent(qemu_egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, -+ qemu_egl_rn_ctx); - #elif defined(CONFIG_EGL) -- eglMakeCurrent(qemu_egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, -- spice_gl_ctx); -+ eglMakeCurrent(qemu_egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, -+ spice_gl_ctx); -+#endif -+ return qemu_egl_create_context(dgc, params); -+ } -+} -+ -+static void qemu_spice_gl_destroy_context(DisplayGLCtx *dgc, QEMUGLContext ctx) -+{ -+ if (spice_opengl == DISPLAY_GL_MODE_CORE) { -+#if defined(HAVE_SPICE_MAC_CGL) -+ spice_cgl_destroy_context(ctx); -+#endif -+ } else { -+ qemu_egl_destroy_context(dgc, ctx); -+ } -+} -+ -+static int qemu_spice_gl_make_context_current(DisplayGLCtx *dgc, -+ QEMUGLContext ctx) -+{ -+ if (spice_opengl == DISPLAY_GL_MODE_CORE) { -+#if defined(HAVE_SPICE_MAC_CGL) -+ return spice_cgl_make_context_current(ctx); - #endif -- return qemu_egl_create_context(dgc, params); -+ } else { -+ return qemu_egl_make_context_current(dgc, ctx); -+ } - } - - static void qemu_spice_gl_scanout_disable(DisplayChangeListener *dcl) -@@ -1160,9 +1300,7 @@ static void qemu_spice_gl_scanout_disable(DisplayChangeListener *dcl) - #if defined(CONFIG_IOSURFACE) - spice_iosurface_destroy(ssd); - #endif --#if defined(CONFIG_EGL) - ssd->tex_id = -1; --#endif - } - - static void qemu_spice_gl_scanout_texture(DisplayChangeListener *dcl, -@@ -1182,10 +1320,8 @@ static void qemu_spice_gl_scanout_texture(DisplayChangeListener *dcl, - fd = egl_get_fd_for_texture(tex_id, &stride, &fourcc, NULL); - #elif defined(CONFIG_IOSURFACE) - if (spice_iosurface_resize(ssd, backing_width, backing_height)) { --#if defined(CONFIG_EGL) - ssd->tex_id = tex_id; - ssd->y_0_top = y_0_top; --#endif - fd = spice_iosurface_create_fd(ssd, &fourcc); - } else { - fd = -1; -@@ -1352,7 +1488,7 @@ static void qemu_spice_gl_update(DisplayChangeListener *dcl, - !y_0_top, false, ptr_x, ptr_y, 1.0, 1.0); - glFlush(); - } --#elif defined(CONFIG_EGL) && defined(CONFIG_IOSURFACE) -+#elif defined(CONFIG_IOSURFACE) - GLuint tex_id = ssd->tex_id; - y_0_top = ssd->y_0_top; - spice_iosurface_blit(ssd, tex_id, !y_0_top); -@@ -1396,8 +1532,8 @@ qemu_spice_is_compatible_dcl(DisplayGLCtx *dgc, - static const DisplayGLCtxOps gl_ctx_ops = { - .dpy_gl_ctx_is_compatible_dcl = qemu_spice_is_compatible_dcl, - .dpy_gl_ctx_create = qemu_spice_gl_create_context, -- .dpy_gl_ctx_destroy = qemu_egl_destroy_context, -- .dpy_gl_ctx_make_current = qemu_egl_make_context_current, -+ .dpy_gl_ctx_destroy = qemu_spice_gl_destroy_context, -+ .dpy_gl_ctx_make_current = qemu_spice_gl_make_context_current, - }; - - #endif /* HAVE_SPICE_GL */ -@@ -1410,7 +1546,7 @@ static void qemu_spice_display_init_one(QemuConsole *con) - - ssd->dcl.ops = &display_listener_ops; - #ifdef HAVE_SPICE_GL -- if (spice_opengl) { -+ if (spice_opengl != DISPLAY_GL_MODE_OFF) { - ssd->dcl.ops = &display_listener_gl_ops; - ssd->dgc.ops = &gl_ctx_ops; - ssd->gl_unblock_bh = qemu_bh_new(qemu_spice_gl_unblock_bh, ssd); -@@ -1425,8 +1561,8 @@ static void qemu_spice_display_init_one(QemuConsole *con) - #endif - #if defined(CONFIG_EGL) - ssd->esurface = EGL_NO_SURFACE; -- ssd->tex_id = -1; - #endif -+ ssd->tex_id = -1; - } - #endif - ssd->dcl.con = con; -@@ -1449,7 +1585,7 @@ static void qemu_spice_display_init_one(QemuConsole *con) - - qemu_spice_create_host_memslot(ssd); - -- if (spice_opengl) { -+ if (spice_opengl != DISPLAY_GL_MODE_OFF) { - qemu_console_set_display_gl_ctx(con, &ssd->dgc); - } - register_displaychangelistener(&ssd->dcl); -@@ -1461,34 +1597,51 @@ void qemu_spice_display_early_init(void) - QemuOptsList *olist = qemu_find_opts("spice"); - QemuOpts *opts = QTAILQ_FIRST(&olist->head); - int port, tls_port; -+ const char *gl_str; -+ bool gl_on = false, gl_core = false; - - port = qemu_opt_get_number(opts, "port", 0); - tls_port = qemu_opt_get_number(opts, "tls-port", 0); -- if (qemu_opt_get_bool(opts, "gl", 0)) { -+ gl_str = qemu_opt_get(opts, "gl"); -+ qapi_bool_parse("", gl_str, &gl_on, NULL); -+ gl_core = g_str_equal(gl_str, "core"); -+ gl_on = gl_on || gl_core || g_str_equal(gl_str, "es"); -+ if (gl_on) { - if ((port != 0) || (tls_port != 0)) { - error_report("SPICE GL support is local-only for now and " - "incompatible with -spice port/tls-port"); - exit(1); - } -+ if (gl_core) { -+#ifdef HAVE_SPICE_MAC_CGL -+ spice_gl_ctx = spice_cgl_create_context(NULL); -+ spice_cgl_make_context_current(spice_gl_ctx); -+ spice_opengl = DISPLAY_GL_MODE_CORE; -+#else -+ error_report("No backend to support SPICE Core GL"); -+ exit(1); -+#endif -+ } else { - #if defined(CONFIG_GBM) -- egl_init(qemu_opt_get(opts, "rendernode"), DISPLAY_GL_MODE_ON, &error_fatal); -+ egl_init(qemu_opt_get(opts, "rendernode"), DISPLAY_GL_MODE_ON, &error_fatal); - #elif defined(CONFIG_EGL) -- if (qemu_egl_init_dpy_cocoa(DISPLAY_GL_MODE_ES)) { -- error_report("SPICE GL failed to initialize ANGLE display"); -- exit(1); -- } -+ if (qemu_egl_init_dpy_cocoa(DISPLAY_GL_MODE_ES)) { -+ error_report("SPICE GL failed to initialize ANGLE display"); -+ exit(1); -+ } - -- spice_gl_ctx = qemu_egl_init_ctx(); -- if (!spice_gl_ctx) { -- error_report("egl: egl_init_ctx failed"); -- exit(1); -- } -+ spice_gl_ctx = qemu_egl_init_ctx(); -+ if (!spice_gl_ctx) { -+ error_report("egl: egl_init_ctx failed"); -+ exit(1); -+ } -+ spice_opengl = DISPLAY_GL_MODE_ES; - #else -- error_report("No backend to support SPICE GL"); -- exit(1); -+ error_report("No backend to support SPICE GL"); -+ exit(1); - #endif -+ } - display_opengl = 1; -- spice_opengl = 1; - } - #endif - } --- -2.41.0 - -From 5ab253b54bf6a4081752253751d0f8958e8bb53f Mon Sep 17 00:00:00 2001 -From: Joelle van Dyne -Date: Sun, 30 Nov 2025 00:07:02 -0800 -Subject: [PATCH 1/9] virtio-gpu-virgl: update virglrenderer defines - -In order to support additional native texture types, we need to update the -defines in virglrenderer. The changes are backwards compatible and so -builds should work with either the new version or the old version. - -https://gitlab.freedesktop.org/virgl/virglrenderer/-/merge_requests/1583 - -Signed-off-by: Joelle van Dyne ---- - hw/display/virtio-gpu-virgl.c | 26 +++++++++++++++++++++++--- - 1 file changed, 23 insertions(+), 3 deletions(-) - -diff --git a/hw/display/virtio-gpu-virgl.c b/hw/display/virtio-gpu-virgl.c -index eac49fbae8..d55c2c1625 100644 ---- a/hw/display/virtio-gpu-virgl.c -+++ b/hw/display/virtio-gpu-virgl.c -@@ -24,6 +24,8 @@ - - #include - -+#define NATIVE_HANDLE_SUPPORT_VERSION (1) -+ - struct virtio_gpu_virgl_resource { - struct virtio_gpu_simple_resource base; - MemoryRegion *mr; -@@ -423,12 +425,28 @@ static void virgl_cmd_set_scanout(VirtIOGPU *g, - memset(&ext, 0, sizeof(ext)); - ret = virgl_renderer_resource_get_info_ext(ss.resource_id, &ext); - info = ext.base; -+ /* fallback to older version */ - native = (ScanoutTextureNative){ - .type = ext.d3d_tex2d ? SCANOUT_TEXTURE_NATIVE_TYPE_D3D : - SCANOUT_TEXTURE_NATIVE_TYPE_NONE, - .handle = ext.d3d_tex2d, - }; --#else -+#if VIRGL_RENDERER_RESOURCE_INFO_EXT_VERSION >= NATIVE_HANDLE_SUPPORT_VERSION -+ if (ext.version >= VIRGL_RENDERER_RESOURCE_INFO_EXT_VERSION) { -+ switch (ext.native_type) { -+ case VIRGL_NATIVE_HANDLE_NONE: -+ case VIRGL_NATIVE_HANDLE_D3D_TEX2D: { -+ /* already handled above */ -+ break; -+ } -+ default: { -+ /* ignore unsupported hint texture type */ -+ break; -+ } -+ } -+ } -+#endif -+#else /* VIRGL_VERSION_MAJOR < 1 */ - memset(&info, 0, sizeof(info)); - ret = virgl_renderer_resource_get_info(ss.resource_id, &info); - #endif -@@ -1107,11 +1125,13 @@ int virtio_gpu_virgl_init(VirtIOGPU *g) - virtio_gpu_3d_cbs.get_egl_display = virgl_get_egl_display; - } - #endif --#ifdef VIRGL_RENDERER_D3D11_SHARE_TEXTURE - if (qemu_egl_angle_native_device) { -+#if defined(VIRGL_RENDERER_NATIVE_SHARE_TEXTURE) -+ flags |= VIRGL_RENDERER_NATIVE_SHARE_TEXTURE; -+#elif defined(VIRGL_RENDERER_D3D11_SHARE_TEXTURE) && defined(WIN32) - flags |= VIRGL_RENDERER_D3D11_SHARE_TEXTURE; -- } - #endif -+ } - #if VIRGL_VERSION_MAJOR >= 1 - if (virtio_gpu_venus_enabled(g->parent_obj.conf)) { - flags |= VIRGL_RENDERER_VENUS | VIRGL_RENDERER_RENDER_SERVER; --- -2.41.0 - -From 5245d2d926c8fcef6bbae0a95b286ed81c42b1b3 Mon Sep 17 00:00:00 2001 -From: Joelle van Dyne -Date: Sun, 30 Nov 2025 00:11:27 -0800 -Subject: [PATCH 2/9] virtio-gpu-virgl: support scanout of Metal textures - -When supported, virglrenderer will return a MTLTexture handle that can be -directly used for scanout. - -Signed-off-by: Joelle van Dyne ---- - hw/display/virtio-gpu-virgl.c | 12 +++++++++++- - hw/display/virtio-gpu.c | 10 ++++++++-- - include/ui/console.h | 1 + - meson.build | 3 +++ - 4 files changed, 23 insertions(+), 3 deletions(-) - -diff --git a/hw/display/virtio-gpu-virgl.c b/hw/display/virtio-gpu-virgl.c -index d55c2c1625..98c9bc4d89 100644 ---- a/hw/display/virtio-gpu-virgl.c -+++ b/hw/display/virtio-gpu-virgl.c -@@ -434,6 +434,13 @@ static void virgl_cmd_set_scanout(VirtIOGPU *g, - #if VIRGL_RENDERER_RESOURCE_INFO_EXT_VERSION >= NATIVE_HANDLE_SUPPORT_VERSION - if (ext.version >= VIRGL_RENDERER_RESOURCE_INFO_EXT_VERSION) { - switch (ext.native_type) { -+#ifdef CONFIG_METAL -+ case VIRGL_NATIVE_HANDLE_METAL_TEXTURE: { -+ native.type = SCANOUT_TEXTURE_NATIVE_TYPE_METAL; -+ native.handle = ext.native_handle; -+ break; -+ } -+#endif - case VIRGL_NATIVE_HANDLE_NONE: - case VIRGL_NATIVE_HANDLE_D3D_TEX2D: { - /* already handled above */ -@@ -1134,7 +1141,10 @@ int virtio_gpu_virgl_init(VirtIOGPU *g) - } - #if VIRGL_VERSION_MAJOR >= 1 - if (virtio_gpu_venus_enabled(g->parent_obj.conf)) { -- flags |= VIRGL_RENDERER_VENUS | VIRGL_RENDERER_RENDER_SERVER; -+ flags |= VIRGL_RENDERER_VENUS; -+#ifndef CONFIG_METAL /* Metal does not support render server */ -+ flags |= VIRGL_RENDERER_RENDER_SERVER; -+#endif - } - #endif - -diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c -index 11a7a85750..1b1e0c4459 100644 ---- a/hw/display/virtio-gpu.c -+++ b/hw/display/virtio-gpu.c -@@ -1471,12 +1471,18 @@ void virtio_gpu_device_realize(DeviceState *qdev, Error **errp) - { - VirtIODevice *vdev = VIRTIO_DEVICE(qdev); - VirtIOGPU *g = VIRTIO_GPU(qdev); -+ bool have_ext_memory; - - if (virtio_gpu_blob_enabled(g->parent_obj.conf)) { -+#ifdef CONFIG_METAL -+ have_ext_memory = virtio_gpu_venus_enabled(g->parent_obj.conf); -+#else -+ have_ext_memory = virtio_gpu_have_udmabuf(); -+#endif - if (!virtio_gpu_rutabaga_enabled(g->parent_obj.conf) && - !virtio_gpu_virgl_enabled(g->parent_obj.conf) && -- !virtio_gpu_have_udmabuf()) { -- error_setg(errp, "need rutabaga or udmabuf for blob resources"); -+ !have_ext_memory) { -+ error_setg(errp, "need rutabaga or ext memory for blob resources"); - return; - } - -diff --git a/include/ui/console.h b/include/ui/console.h -index 445e563150..f96565d109 100644 ---- a/include/ui/console.h -+++ b/include/ui/console.h -@@ -134,6 +134,7 @@ struct QemuConsoleClass { - typedef enum ScanoutTextureNativeType { - SCANOUT_TEXTURE_NATIVE_TYPE_NONE, - SCANOUT_TEXTURE_NATIVE_TYPE_D3D, -+ SCANOUT_TEXTURE_NATIVE_TYPE_METAL, - } ScanoutTextureNativeType; - - typedef struct ScanoutTextureNative { -diff --git a/meson.build b/meson.build -index af9284fb29..3ecd4d5b3a 100644 ---- a/meson.build -+++ b/meson.build -@@ -836,6 +836,7 @@ coref = [] - iokit = [] - iosurface = not_found - pvg = not_found -+metal = not_found - quartzcore = not_found - emulator_link_args = [] - midl = not_found -@@ -861,6 +862,7 @@ elif host_os == 'darwin' - host_dsosuf = '.dylib' - pvg = dependency('appleframeworks', modules: ['ParavirtualizedGraphics', 'Metal'], - required: get_option('pvg')) -+ metal = dependency('appleframeworks', modules: 'Metal', required: false) - quartzcore = dependency('appleframeworks', modules: ['OpenGL', 'QuartzCore'], required: false) - elif host_os == 'sunos' - socket = [cc.find_library('socket'), -@@ -2627,6 +2629,7 @@ if xen.found() - ('0' + xen_version[2]).substring(-2) - config_host_data.set('CONFIG_XEN_CTRL_INTERFACE_VERSION', xen_ctrl_version) - endif -+config_host_data.set('CONFIG_METAL', metal.found()) - config_host_data.set('QEMU_VERSION', '"@0@"'.format(meson.project_version())) - config_host_data.set('QEMU_VERSION_MAJOR', meson.project_version().split('.')[0]) - config_host_data.set('QEMU_VERSION_MINOR', meson.project_version().split('.')[1]) --- -2.41.0 - -From b2f9af64f986aa44a6f1171e97fc665cc55ee0ff Mon Sep 17 00:00:00 2001 -From: Joelle van Dyne -Date: Sun, 30 Nov 2025 15:27:49 -0800 -Subject: [PATCH 3/9] console: add cleanup callback for ScanoutTexture - -Before we introduce changes that allow for QemuConsole to take ownership -of a texture handle, we need scaffolding that will allow us to callback -into a cleanup function any time the ScanoutTexture becomes invalid, which -is whenever the `scanout.kind` or `scanout.texture` gets updated. - -The ordering is important: we need to first update the DisplayScanout, -then we need to notify all the listeners, and once all the listeners have -had the chance to finish using the previous native texture, we are safe to -call the cleanup function. This means we need to hold on to the previous -scanout native handle locally until all listeners are notified. - -Signed-off-by: Joelle van Dyne ---- - hw/display/virtio-gpu-virgl.c | 2 +- - include/ui/console.h | 9 +++++- - ui/console.c | 56 +++++++++++++++++++++++++++++++---- - 3 files changed, 59 insertions(+), 8 deletions(-) - -diff --git a/hw/display/virtio-gpu-virgl.c b/hw/display/virtio-gpu-virgl.c -index 98c9bc4d89..8d511ed562 100644 ---- a/hw/display/virtio-gpu-virgl.c -+++ b/hw/display/virtio-gpu-virgl.c -@@ -472,7 +472,7 @@ static void virgl_cmd_set_scanout(VirtIOGPU *g, - info.flags & VIRTIO_GPU_RESOURCE_FLAG_Y_0_TOP, - info.width, info.height, - ss.r.x, ss.r.y, ss.r.width, ss.r.height, -- native); -+ native, NULL); - } else { - dpy_gfx_replace_surface( - g->parent_obj.scanout[ss.scanout_id].con, NULL); -diff --git a/include/ui/console.h b/include/ui/console.h -index f96565d109..8e7364cfde 100644 ---- a/include/ui/console.h -+++ b/include/ui/console.h -@@ -146,6 +146,11 @@ typedef struct ScanoutTextureNative { - .type = SCANOUT_TEXTURE_NATIVE_TYPE_NONE \ - }) - -+/** -+ * Cleanup callback function when ScanoutTexture is about to be destroyed -+ */ -+typedef void (*ScanoutTextureCleanup)(ScanoutTextureNative *native); -+ - typedef struct ScanoutTexture { - uint32_t backing_id; - bool backing_y_0_top; -@@ -156,6 +161,7 @@ typedef struct ScanoutTexture { - uint32_t width; - uint32_t height; - ScanoutTextureNative native; -+ ScanoutTextureCleanup cb_cleanup; - } ScanoutTexture; - - typedef struct QemuUIInfo { -@@ -344,7 +350,8 @@ void dpy_gl_scanout_texture(QemuConsole *con, - uint32_t backing_id, bool backing_y_0_top, - uint32_t backing_width, uint32_t backing_height, - uint32_t x, uint32_t y, uint32_t w, uint32_t h, -- ScanoutTextureNative native); -+ ScanoutTextureNative native, -+ ScanoutTextureCleanup cb_cleanup); - void dpy_gl_scanout_dmabuf(QemuConsole *con, - QemuDmaBuf *dmabuf); - void dpy_gl_cursor_dmabuf(QemuConsole *con, QemuDmaBuf *dmabuf, -diff --git a/ui/console.c b/ui/console.c -index d8d58fc14a..f6d7879eff 100644 ---- a/ui/console.c -+++ b/ui/console.c -@@ -808,6 +808,41 @@ void dpy_gfx_update_full(QemuConsole *con) - dpy_gfx_update(con, 0, 0, w, h); - } - -+typedef struct ScanoutChange { -+ ScanoutTextureNative native; -+ ScanoutTextureCleanup cb_cleanup; -+} ScanoutChange; -+ -+#define SCANOUT_CHANGE_NONE ((ScanoutChange){ NO_NATIVE_TEXTURE }) -+ -+static ScanoutChange dpy_change_scanout_kind(DisplayScanout *scanout, -+ enum display_scanout kind) -+{ -+ ScanoutChange change = SCANOUT_CHANGE_NONE; -+ -+ /** -+ * We cannot cleanup until the resource is no longer in use, so we record it -+ * You MUST call dpy_complete_scanout_change after all listeners are updated -+ */ -+ if (scanout->kind == SCANOUT_TEXTURE && scanout->texture.cb_cleanup) { -+ change.native = scanout->texture.native; -+ change.cb_cleanup = scanout->texture.cb_cleanup; -+ } -+ scanout->kind = kind; -+ -+ return change; -+} -+ -+static void dpy_complete_scanout_change(ScanoutChange *change) -+{ -+ /** -+ * If we previously have a texture and cleanup is required, we call it now -+ */ -+ if (change->native.type != SCANOUT_TEXTURE_NATIVE_TYPE_NONE && change->cb_cleanup) { -+ change->cb_cleanup(&change->native); -+ } -+} -+ - void dpy_gfx_replace_surface(QemuConsole *con, - DisplaySurface *surface) - { -@@ -818,6 +853,7 @@ void dpy_gfx_replace_surface(QemuConsole *con, - DisplayChangeListener *dcl; - int width; - int height; -+ ScanoutChange change = SCANOUT_CHANGE_NONE; - - if (!surface) { - if (old_surface) { -@@ -833,7 +869,7 @@ void dpy_gfx_replace_surface(QemuConsole *con, - - assert(old_surface != new_surface); - -- con->scanout.kind = SCANOUT_SURFACE; -+ change = dpy_change_scanout_kind(&con->scanout, SCANOUT_SURFACE); - con->surface = new_surface; - dpy_gfx_create_texture(con, new_surface); - QLIST_FOREACH(dcl, &s->listeners, next) { -@@ -844,6 +880,7 @@ void dpy_gfx_replace_surface(QemuConsole *con, - } - dpy_gfx_destroy_texture(con, old_surface); - qemu_free_displaysurface(old_surface); -+ dpy_complete_scanout_change(&change); - } - - bool dpy_gfx_check_format(QemuConsole *con, -@@ -1002,9 +1039,10 @@ void dpy_gl_scanout_disable(QemuConsole *con) - { - DisplayState *s = con->ds; - DisplayChangeListener *dcl; -+ ScanoutChange change = SCANOUT_CHANGE_NONE; - - if (con->scanout.kind != SCANOUT_SURFACE) { -- con->scanout.kind = SCANOUT_NONE; -+ change = dpy_change_scanout_kind(&con->scanout, SCANOUT_NONE); - } - QLIST_FOREACH(dcl, &s->listeners, next) { - if (con != dcl->con) { -@@ -1014,6 +1052,7 @@ void dpy_gl_scanout_disable(QemuConsole *con) - dcl->ops->dpy_gl_scanout_disable(dcl); - } - } -+ dpy_complete_scanout_change(&change); - } - - void dpy_gl_scanout_texture(QemuConsole *con, -@@ -1023,15 +1062,17 @@ void dpy_gl_scanout_texture(QemuConsole *con, - uint32_t backing_height, - uint32_t x, uint32_t y, - uint32_t width, uint32_t height, -- ScanoutTextureNative native) -+ ScanoutTextureNative native, -+ ScanoutTextureCleanup cb_cleanup) - { - DisplayState *s = con->ds; - DisplayChangeListener *dcl; -+ ScanoutChange change = SCANOUT_CHANGE_NONE; - -- con->scanout.kind = SCANOUT_TEXTURE; -+ change = dpy_change_scanout_kind(&con->scanout, SCANOUT_TEXTURE); - con->scanout.texture = (ScanoutTexture) { - backing_id, backing_y_0_top, backing_width, backing_height, -- x, y, width, height, native, -+ x, y, width, height, native, cb_cleanup - }; - QLIST_FOREACH(dcl, &s->listeners, next) { - if (con != dcl->con) { -@@ -1045,6 +1086,7 @@ void dpy_gl_scanout_texture(QemuConsole *con, - native); - } - } -+ dpy_complete_scanout_change(&change); - } - - void dpy_gl_scanout_dmabuf(QemuConsole *con, -@@ -1052,8 +1094,9 @@ void dpy_gl_scanout_dmabuf(QemuConsole *con, - { - DisplayState *s = con->ds; - DisplayChangeListener *dcl; -+ ScanoutChange change = SCANOUT_CHANGE_NONE; - -- con->scanout.kind = SCANOUT_DMABUF; -+ change = dpy_change_scanout_kind(&con->scanout, SCANOUT_DMABUF); - con->scanout.dmabuf = dmabuf; - QLIST_FOREACH(dcl, &s->listeners, next) { - if (con != dcl->con) { -@@ -1063,6 +1106,7 @@ void dpy_gl_scanout_dmabuf(QemuConsole *con, - dcl->ops->dpy_gl_scanout_dmabuf(dcl, dmabuf); - } - } -+ dpy_complete_scanout_change(&change); - } - - void dpy_gl_cursor_dmabuf(QemuConsole *con, QemuDmaBuf *dmabuf, --- -2.41.0 - -From 4206bd1f7083ad5d1bacae48f37ecdcba123ee2c Mon Sep 17 00:00:00 2001 -From: Joelle van Dyne -Date: Sun, 30 Nov 2025 15:50:56 -0800 -Subject: [PATCH 4/9] virtio-gpu-virgl: add support for native blob scanout - -On macOS we do not have dmabuf and so we use MTLTexture as our scanout -source. For blob scanout, the buffer is untyped and so we cannot get a -MTLTexture until we pass more information to virglrenderer (surface size, -pixel format, etc). The new API to do this is currently unstable so we -need to define `VIRGL_RENDERER_UNSTABLE_APIS`. This should be removed after -the ABI becomes stable. - -Signed-off-by: Joelle van Dyne ---- - hw/display/virtio-gpu-virgl.c | 60 +++++++++++++++++++++++++++++++++++ - meson.build | 5 +++ - 2 files changed, 65 insertions(+) - -diff --git a/hw/display/virtio-gpu-virgl.c b/hw/display/virtio-gpu-virgl.c -index 8d511ed562..f515165335 100644 ---- a/hw/display/virtio-gpu-virgl.c -+++ b/hw/display/virtio-gpu-virgl.c -@@ -22,6 +22,7 @@ - - #include "ui/egl-helpers.h" - -+#define VIRGL_RENDERER_UNSTABLE_APIS - #include - - #define NATIVE_HANDLE_SUPPORT_VERSION (1) -@@ -828,6 +829,59 @@ static void virgl_cmd_resource_unmap_blob(VirtIOGPU *g, - } - } - -+#if defined(HAVE_VIRGL_RENDERER_NATIVE_SCANOUT) -+static void virgl_scanout_native_blob_cleanup(ScanoutTextureNative *native) -+{ -+ assert(native->type == SCANOUT_TEXTURE_NATIVE_TYPE_METAL); -+ virgl_renderer_release_handle_for_scanout(VIRGL_NATIVE_HANDLE_METAL_TEXTURE, -+ native->handle); -+} -+ -+static bool virgl_scanout_native_blob(VirtIOGPU *g, -+ struct virtio_gpu_set_scanout_blob *ss) -+{ -+ struct virtio_gpu_scanout *scanout = &g->parent_obj.scanout[ss->scanout_id]; -+ enum virgl_renderer_native_handle_type type; -+ virgl_renderer_native_handle handle; -+ ScanoutTextureNative native; -+ -+ type = virgl_renderer_create_handle_for_scanout(ss->resource_id, -+ ss->width, -+ ss->height, -+ ss->format, -+ ss->padding, -+ ss->strides[0], -+ ss->offsets[0], -+ &handle); -+#ifdef CONFIG_METAL -+ if (type == VIRGL_NATIVE_HANDLE_METAL_TEXTURE) { -+ native = (ScanoutTextureNative){ -+ .type = SCANOUT_TEXTURE_NATIVE_TYPE_METAL, -+ .handle = handle, -+ }; -+ qemu_console_resize(scanout->con, -+ ss->r.width, ss->r.height); -+ dpy_gl_scanout_texture( -+ scanout->con, 0, -+ false, -+ ss->width, ss->height, -+ ss->r.x, ss->r.y, ss->r.width, ss->r.height, -+ native, virgl_scanout_native_blob_cleanup); -+ scanout->resource_id = ss->resource_id; -+ -+ return true; -+ } -+#endif -+ -+ /* don't leak memory if handle type is unknown */ -+ if (type != VIRGL_NATIVE_HANDLE_NONE) { -+ virgl_renderer_release_handle_for_scanout(type, handle); -+ } -+ -+ return false; -+} -+#endif -+ - static void virgl_cmd_set_scanout_blob(VirtIOGPU *g, - struct virtio_gpu_ctrl_command *cmd) - { -@@ -866,6 +920,12 @@ static void virgl_cmd_set_scanout_blob(VirtIOGPU *g, - return; - } - -+#if defined(HAVE_VIRGL_RENDERER_NATIVE_SCANOUT) -+ if (virgl_scanout_native_blob(g, &ss)) { -+ return; -+ } -+#endif -+ - res = virtio_gpu_virgl_find_resource(g, ss.resource_id); - if (!res) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: resource does not exist %d\n", -diff --git a/meson.build b/meson.build -index 3ecd4d5b3a..84ee2f179d 100644 ---- a/meson.build -+++ b/meson.build -@@ -2584,6 +2584,11 @@ config_host_data.set('CONFIG_VNC', vnc.found()) - config_host_data.set('CONFIG_VNC_JPEG', jpeg.found()) - config_host_data.set('CONFIG_VNC_SASL', sasl.found()) - if virgl.found() -+ config_host_data.set('HAVE_VIRGL_RENDERER_NATIVE_SCANOUT', -+ cc.has_function('virgl_renderer_create_handle_for_scanout', -+ args: '-DVIRGL_RENDERER_UNSTABLE_APIS', -+ prefix: '#include ', -+ dependencies: virgl)) - config_host_data.set('VIRGL_VERSION_MAJOR', virgl.version().split('.')[0]) - endif - config_host_data.set('CONFIG_VIRTFS', have_virtfs) --- -2.41.0 - -From 068267f42709a93f9fabc57b05225e18026f5a97 Mon Sep 17 00:00:00 2001 -From: Joelle van Dyne -Date: Sun, 30 Nov 2025 15:56:50 -0800 -Subject: [PATCH 5/9] hvf: warn when attempting to add unaligned page size - -Instead of silently failing, we should print a warning so it is clear why -a future guest access to that location might cause an exception. ---- - accel/hvf/hvf-accel-ops.c | 8 ++++++++ - 1 file changed, 8 insertions(+) - -diff --git a/accel/hvf/hvf-accel-ops.c b/accel/hvf/hvf-accel-ops.c -index da719d85b7..dae9600f4b 100644 ---- a/accel/hvf/hvf-accel-ops.c -+++ b/accel/hvf/hvf-accel-ops.c -@@ -139,6 +139,14 @@ static void hvf_set_phys_mem(MemoryRegionSection *section, bool add) - - if (!QEMU_IS_ALIGNED(int128_get64(section->size), page_size) || - !QEMU_IS_ALIGNED(section->offset_within_address_space, page_size)) { -+ if (add) { -+ warn_report("Cannot add 0x%016llX:0x%016llX because it is not " -+ "aligned to page size (0x%X)", -+ section->offset_within_address_space, -+ section->offset_within_address_space + -+ int128_get64(section->size), -+ (uint32_t)page_size); -+ } - /* Not page aligned, so we can not map as RAM */ - add = false; - } --- -2.41.0 - -From 09ce55779cd608595ea6b5bebf1b426136f45fa0 Mon Sep 17 00:00:00 2001 -From: Joelle van Dyne -Date: Sun, 30 Nov 2025 15:59:10 -0800 -Subject: [PATCH 6/9] hvf: allow slot size to increase - -Instead of having a fixed limit of 32 slots, we allow the number of slots -to expand. Currently there does not seem to be a need to add a limit, but -if the allocation fails, we will abort. The KVM backend was used for -inspiration here. ---- - accel/hvf/hvf-accel-ops.c | 79 ++++++++++++++++++++++++++++++--------- - include/system/hvf_int.h | 12 +++++- - 2 files changed, 72 insertions(+), 19 deletions(-) - -diff --git a/accel/hvf/hvf-accel-ops.c b/accel/hvf/hvf-accel-ops.c -index dae9600f4b..13cc72baaf 100644 ---- a/accel/hvf/hvf-accel-ops.c -+++ b/accel/hvf/hvf-accel-ops.c -@@ -67,6 +67,57 @@ bool hvf_tso_mode = 0; - - /* Memory slots */ - -+/* Default num of memslots to be allocated when VM starts */ -+#define HVF_MEMSLOTS_NUM_ALLOC_DEFAULT 32 -+ -+static bool hvf_slots_grow(HVFState *state, unsigned int num_slots_new) -+{ -+ unsigned int i, cur = state->num_slots; -+ hvf_slot *slots; -+ hvf_mac_slot *mac_slots; -+ -+ assert(num_slots_new > cur); -+ if (cur == 0) { -+ slots = g_new0(hvf_slot, num_slots_new); -+ if (!slots) { -+ return false; -+ } -+ mac_slots = g_new0(hvf_mac_slot, num_slots_new); -+ if (!mac_slots) { -+ g_free(slots); -+ return false; -+ } -+ } else { -+ slots = g_renew(hvf_slot, state->slots, num_slots_new); -+ if (!slots) { -+ return false; -+ } -+ mac_slots = g_renew(hvf_mac_slot, state->mac_slots, num_slots_new); -+ if (!mac_slots) { -+ /* save allocated slots but not new size */ -+ state->slots = slots; -+ return false; -+ } -+ /* -+ * g_renew() doesn't initialize extended buffers, however hvf -+ * memslots require fields to be zero-initialized. E.g. pointers, -+ * memory_size field, etc. -+ */ -+ memset(&slots[cur], 0x0, sizeof(slots[0]) * (num_slots_new - cur)); -+ memset(&mac_slots[cur], 0x0, sizeof(mac_slots[0]) * (num_slots_new - cur)); -+ } -+ -+ for (i = cur; i < num_slots_new; i++) { -+ slots[i].slot_id = i; -+ } -+ -+ state->slots = slots; -+ state->mac_slots = mac_slots; -+ state->num_slots = num_slots_new; -+ -+ return true; -+} -+ - hvf_slot *hvf_find_overlap_slot(uint64_t start, uint64_t size) - { - hvf_slot *slot; -@@ -81,21 +132,12 @@ hvf_slot *hvf_find_overlap_slot(uint64_t start, uint64_t size) - return NULL; - } - --struct mac_slot { -- int present; -- uint64_t size; -- uint64_t gpa_start; -- uint64_t gva; --}; -- --struct mac_slot mac_slots[32]; -- - static int do_hvf_set_memory(hvf_slot *slot, hv_memory_flags_t flags) - { -- struct mac_slot *macslot; -+ hvf_mac_slot *macslot; - hv_return_t ret; - -- macslot = &mac_slots[slot->slot_id]; -+ macslot = &hvf_state->mac_slots[slot->slot_id]; - - if (macslot->present) { - if (macslot->size != slot->size) { -@@ -195,8 +237,11 @@ static void hvf_set_phys_mem(MemoryRegionSection *section, bool add) - } - - if (x == hvf_state->num_slots) { -- error_report("No free slots"); -- abort(); -+ if (!hvf_slots_grow(hvf_state, hvf_state->num_slots * 2)) { -+ error_report("Cannot allocate any more slots"); -+ abort(); -+ } -+ mem = &hvf_state->slots[x]; - } - - mem->size = int128_get64(section->size); -@@ -333,6 +378,7 @@ static int hvf_accel_init(MachineState *ms) - HVFState *s; - int pa_range = 36; - MachineClass *mc = MACHINE_GET_CLASS(ms); -+ bool success; - - if (mc->hvf_get_physical_address_range) { - pa_range = mc->hvf_get_physical_address_range(ms); -@@ -346,11 +392,8 @@ static int hvf_accel_init(MachineState *ms) - - s = g_new0(HVFState, 1); - -- s->num_slots = ARRAY_SIZE(s->slots); -- for (x = 0; x < s->num_slots; ++x) { -- s->slots[x].size = 0; -- s->slots[x].slot_id = x; -- } -+ success = hvf_slots_grow(s, HVF_MEMSLOTS_NUM_ALLOC_DEFAULT); -+ assert(success); - - QTAILQ_INIT(&s->hvf_sw_breakpoints); - -diff --git a/include/system/hvf_int.h b/include/system/hvf_int.h -index 763201dd71..52c69ed9d5 100644 ---- a/include/system/hvf_int.h -+++ b/include/system/hvf_int.h -@@ -37,6 +37,7 @@ extern hv_return_t _hv_vcpu_set_actlr(hv_vcpu_t vcpu, uint64_t value); - /* hvf_slot flags */ - #define HVF_SLOT_LOG (1 << 0) - -+/* Represent memory logically mapped by QEMU */ - typedef struct hvf_slot { - uint64_t start; - uint64_t size; -@@ -46,6 +47,14 @@ typedef struct hvf_slot { - MemoryRegion *region; - } hvf_slot; - -+/* Represent memory currently mapped in HVF */ -+typedef struct hvf_mac_slot { -+ int present; -+ uint64_t size; -+ uint64_t gpa_start; -+ uint64_t gva; -+} hvf_mac_slot; -+ - typedef struct hvf_vcpu_caps { - uint64_t vmx_cap_pinbased; - uint64_t vmx_cap_procbased; -@@ -57,7 +66,8 @@ typedef struct hvf_vcpu_caps { - - struct HVFState { - AccelState parent; -- hvf_slot slots[32]; -+ hvf_slot *slots; -+ hvf_mac_slot *mac_slots; - int num_slots; - - hvf_vcpu_caps *hvf_caps; --- -2.41.0 - -From 3f775e8c8540bf6193e5f97202602fe54433fc61 Mon Sep 17 00:00:00 2001 -From: Joelle van Dyne -Date: Fri, 19 Dec 2025 07:07:12 -0800 -Subject: [PATCH] hvf: support changing IPA granule size - -The IPA granule is the smallest page size hv_vm_map() support. For Venus, we -need to support 4KiB pages. macOS 26 introduces a public API for setting -the granule size. We can only use this when compiled with macOS 26 SDK and -run on macOS 26+. Otherwise, we fall back to an older, private, API which -achieves the same purpose. - -Signed-off-by: Joelle van Dyne ---- - accel/hvf/hvf-accel-ops.c | 43 ++++++++++++++++++++++++++++++++- - include/system/hvf_int.h | 4 +++- - target/arm/hvf/hvf.c | 50 ++++++++++++++++++++++++++++++++++++++- - target/i386/hvf/hvf.c | 10 +++++++- - 4 files changed, 103 insertions(+), 4 deletions(-) - -diff --git a/accel/hvf/hvf-accel-ops.c b/accel/hvf/hvf-accel-ops.c -index 13cc72baaf..036c121adb 100644 ---- a/accel/hvf/hvf-accel-ops.c -+++ b/accel/hvf/hvf-accel-ops.c -@@ -54,6 +54,8 @@ - #include "exec/exec-all.h" - #include "gdbstub/enums.h" - #include "hw/boards.h" -+#include "qapi/error.h" -+#include "qapi/visitor.h" - #include "system/accel-ops.h" - #include "system/cpus.h" - #include "system/hvf.h" -@@ -64,6 +66,7 @@ - - HVFState *hvf_state; - bool hvf_tso_mode = 0; -+uint32_t hvf_ipa_granule_size = 0; - - /* Memory slots */ - -@@ -179,6 +182,10 @@ static void hvf_set_phys_mem(MemoryRegionSection *section, bool add) - } - } - -+ if (hvf_ipa_granule_size) { -+ page_size = hvf_ipa_granule_size; -+ } -+ - if (!QEMU_IS_ALIGNED(int128_get64(section->size), page_size) || - !QEMU_IS_ALIGNED(section->offset_within_address_space, page_size)) { - if (add) { -@@ -387,7 +394,7 @@ static int hvf_accel_init(MachineState *ms) - } - } - -- ret = hvf_arch_vm_create(ms, (uint32_t)pa_range); -+ ret = hvf_arch_vm_create(ms, (uint32_t)pa_range, hvf_ipa_granule_size); - assert_hvf_ok(ret); - - s = g_new0(HVFState, 1); -@@ -422,6 +429,34 @@ static inline int hvf_gdbstub_sstep_flags(void) - return SSTEP_ENABLE | SSTEP_NOIRQ; - } - -+static void hvf_get_ipa_granule_size(Object *obj, Visitor *v, -+ const char *name, void *opaque, -+ Error **errp) -+{ -+ HVFState *s = HVF_STATE(obj); -+ uint32_t value = hvf_ipa_granule_size; -+ -+ visit_type_uint32(v, name, &value, errp); -+} -+ -+static void hvf_set_ipa_granule_size(Object *obj, Visitor *v, -+ const char *name, void *opaque, -+ Error **errp) -+{ -+ HVFState *s = HVF_STATE(obj); -+ uint32_t value; -+ -+ if (!visit_type_uint32(v, name, &value, errp)) { -+ return; -+ } -+ if (value & (value - 1)) { -+ error_setg(errp, "ipa-granule-size must be a power of two."); -+ return; -+ } -+ -+ hvf_ipa_granule_size = value; -+} -+ - static void hvf_accel_class_init(ObjectClass *oc, void *data) - { - AccelClass *ac = ACCEL_CLASS(oc); -@@ -436,6 +471,12 @@ static void hvf_accel_class_init(ObjectClass *oc, void *data) - object_class_property_set_description(oc, "tso", - "Set on/off to enable/disable total store ordering mode"); - #endif -+ -+ object_class_property_add(oc, "ipa-granule-size", "uint32", -+ hvf_get_ipa_granule_size, hvf_set_ipa_granule_size, -+ NULL, NULL); -+ object_class_property_set_description(oc, "ipa-granule-size", -+ "Size of a single guest page"); - } - - static const TypeInfo hvf_accel_type = { -diff --git a/include/system/hvf_int.h b/include/system/hvf_int.h -index 52c69ed9d5..93e1b23e1a 100644 ---- a/include/system/hvf_int.h -+++ b/include/system/hvf_int.h -@@ -76,6 +76,7 @@ struct HVFState { - }; - extern HVFState *hvf_state; - extern bool hvf_tso_mode; -+extern uint32_t hvf_ipa_granule_size; - - struct AccelCPUState { - hvf_vcpuid fd; -@@ -91,7 +92,8 @@ void assert_hvf_ok_impl(hv_return_t ret, const char *file, unsigned int line, - #define assert_hvf_ok(EX) assert_hvf_ok_impl((EX), __FILE__, __LINE__, #EX) - const char *hvf_return_string(hv_return_t ret); - int hvf_arch_init(void); --hv_return_t hvf_arch_vm_create(MachineState *ms, uint32_t pa_range); -+hv_return_t hvf_arch_vm_create(MachineState *ms, uint32_t pa_range, -+ uint32_t ipa_granule_size); - int hvf_arch_init_vcpu(CPUState *cpu); - void hvf_arch_vcpu_destroy(CPUState *cpu); - int hvf_vcpu_exec(CPUState *); -diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c -index aa63f30fcf..51da8009cb 100644 ---- a/target/arm/hvf/hvf.c -+++ b/target/arm/hvf/hvf.c -@@ -12,6 +12,9 @@ - #include "qemu/osdep.h" - #include "qemu/error-report.h" - #include "qemu/log.h" -+#include -+#include -+#include - - #include "system/runstate.h" - #include "system/hvf.h" -@@ -1035,7 +1038,45 @@ void hvf_arch_vcpu_destroy(CPUState *cpu) - { - } - --hv_return_t hvf_arch_vm_create(MachineState *ms, uint32_t pa_range) -+static hv_return_t hvf_set_ipa_granule(hv_vm_config_t config, -+ uint32_t ipa_granule_size) -+{ -+ static hv_return_t (*set_ipa_granule)(hv_vm_config_t, uint32_t); -+ uint64_t page_size = qemu_real_host_page_size(); -+ -+ /* macOS 26 introduces a public API for setting granule size */ -+#if defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && defined(__MAC_26_0) && \ -+ __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_26_0 -+ if (__builtin_available(macOS 26, *)) { -+ hv_ipa_granule_t granule = HV_IPA_GRANULE_16KB; -+ -+ if (ipa_granule_size == 4096) { -+ granule = HV_IPA_GRANULE_4KB; -+ } else if (ipa_granule_size != 16384) { -+ error_report("Unsupported granule size: 0x%x", ipa_granule_size); -+ return HV_UNSUPPORTED; -+ } -+ -+ return hv_vm_config_set_ipa_granule(config, granule); -+ } -+#endif -+ -+ /* older macOS need to use a private API */ -+ if (!set_ipa_granule) { -+ set_ipa_granule = dlsym(RTLD_NEXT, "_hv_vm_config_set_ipa_granule"); -+ } -+ if (set_ipa_granule) { -+ return set_ipa_granule(config, ipa_granule_size); -+ } else if (ipa_granule_size != page_size) { -+ error_report("Failed to find _hv_vm_config_set_ipa_granule"); -+ return HV_UNSUPPORTED; -+ } -+ -+ return HV_SUCCESS; -+} -+ -+hv_return_t hvf_arch_vm_create(MachineState *ms, uint32_t pa_range, -+ uint32_t ipa_granule_size) - { - hv_return_t ret; - hv_vm_config_t config = NULL; -@@ -1056,6 +1097,13 @@ hv_return_t hvf_arch_vm_create(MachineState *ms, uint32_t pa_range) - } - #endif - -+ if (ipa_granule_size) { -+ ret = hvf_set_ipa_granule(config, ipa_granule_size); -+ if (ret != HV_SUCCESS) { -+ goto cleanup; -+ } -+ } -+ - ret = hv_vm_create(config); - - cleanup: -diff --git a/target/i386/hvf/hvf.c b/target/i386/hvf/hvf.c -index 9ba0e04ac7..3cfe84313c 100644 ---- a/target/i386/hvf/hvf.c -+++ b/target/i386/hvf/hvf.c -@@ -224,8 +224,16 @@ int hvf_arch_init(void) - return 0; - } - --hv_return_t hvf_arch_vm_create(MachineState *ms, uint32_t pa_range) -+hv_return_t hvf_arch_vm_create(MachineState *ms, uint32_t pa_range, -+ uint32_t ipa_granule_size) - { -+ uint64_t page_size = qemu_real_host_page_size(); -+ -+ if (ipa_granule_size != 0 && ipa_granule_size != page_size) { -+ error_report("Only supported IPA granule size: 0x%llx", page_size); -+ return HV_UNSUPPORTED; -+ } -+ - return hv_vm_create(HV_VM_DEFAULT); - } - --- -2.41.0 - -From 1cafc035c4f10591d6064d76568c41249ab819ff Mon Sep 17 00:00:00 2001 -From: Joelle van Dyne -Date: Mon, 22 Dec 2025 06:04:46 -0800 -Subject: [PATCH 8/9] virtio-gpu-virgl: correct parent for blob memory region - -When `owner` == `mr`, `object_unparent` will crash: - -object_unparent(mr) -> -object_property_del_child(mr, mr) -> -object_finalize_child_property(mr, name, mr) -> -object_unref(mr) -> -object_finalize(mr) -> -object_property_del_all(mr) -> -object_finalize_child_property(mr, name, mr) -> -object_unref(mr) -> -fail on g_assert(obj->ref > 0) - -However, passing a different `owner` to `memory_region_init` does not -work. `memory_region_ref` has an optimization where it takes a ref -only on the owner. That means when flatviews are created, it does not -take a ref on the region and you can get a UAF from `flatview_destroy` -called from RCU. - -The correct fix therefore is to use `NULL` as the name which will set -the `owner` but not the `parent` (which is still NULL). This allows us -to use `memory_region_ref` on itself while not having to rely on unparent -for cleanup. - -Signed-off-by: Joelle van Dyne ---- - hw/display/virtio-gpu-virgl.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/hw/display/virtio-gpu-virgl.c b/hw/display/virtio-gpu-virgl.c -index f515165335..6a97bdc67a 100644 ---- a/hw/display/virtio-gpu-virgl.c -+++ b/hw/display/virtio-gpu-virgl.c -@@ -123,7 +123,7 @@ virtio_gpu_virgl_map_resource_blob(VirtIOGPU *g, - vmr->g = g; - - mr = &vmr->mr; -- memory_region_init_ram_ptr(mr, OBJECT(mr), "blob", size, data); -+ memory_region_init_ram_ptr(mr, OBJECT(mr), NULL, size, data); - memory_region_add_subregion(&b->hostmem, offset, mr); - memory_region_set_enabled(mr, true); - -@@ -185,7 +185,7 @@ virtio_gpu_virgl_unmap_resource_blob(VirtIOGPU *g, - /* memory region owns self res->mr object and frees it by itself */ - memory_region_set_enabled(mr, false); - memory_region_del_subregion(&b->hostmem, mr); -- object_unparent(OBJECT(mr)); -+ object_unref(OBJECT(mr)); - } - - return 0; --- -2.41.0 - -From fb99e056ac794d77e2c7c00afc50d53205099fc1 Mon Sep 17 00:00:00 2001 -From: osy -Date: Wed, 7 Jan 2026 12:18:14 -0800 -Subject: [PATCH 9/9] ui/spice-display: support MTLTexture scanout to IOSurface - ---- - include/ui/spice-display.h | 30 +++++++ - ui/egl-helpers.c | 29 ++++++- - ui/meson.build | 4 + - ui/spice-display-metal.m | 166 +++++++++++++++++++++++++++++++++++++ - ui/spice-display.c | 86 ++++++++++++++++++- - 5 files changed, 312 insertions(+), 3 deletions(-) - create mode 100644 ui/spice-display-metal.m - -diff --git a/include/ui/spice-display.h b/include/ui/spice-display.h -index ec55194139..8e752f67da 100644 ---- a/include/ui/spice-display.h -+++ b/include/ui/spice-display.h -@@ -77,6 +77,13 @@ typedef struct QXLCookie { - QXLRect area; - int redraw; - } render; -+ struct { -+ QXLInstance *qxl; -+ uint32_t x; -+ uint32_t y; -+ uint32_t w; -+ uint32_t h; -+ } gl_draw; - void *data; - } u; - } QXLCookie; -@@ -86,6 +93,7 @@ QXLCookie *qxl_cookie_new(int type, uint64_t io); - typedef struct SimpleSpiceDisplay SimpleSpiceDisplay; - typedef struct SimpleSpiceUpdate SimpleSpiceUpdate; - typedef struct SimpleSpiceCursor SimpleSpiceCursor; -+typedef void *SpiceDisplayMetalContext; - - struct SimpleSpiceDisplay { - DisplaySurface *ds; -@@ -137,6 +145,9 @@ struct SimpleSpiceDisplay { - #if defined(CONFIG_IOSURFACE) - IOSurfaceRef iosurface; - int surface_send_fd; -+#if defined(CONFIG_METAL) -+ SpiceDisplayMetalContext metal_context; -+#endif - #endif - #if defined(CONFIG_EGL) - EGLSurface esurface; -@@ -197,4 +208,23 @@ void qemu_spice_display_start(void); - void qemu_spice_display_stop(void); - int qemu_spice_display_is_running(SimpleSpiceDisplay *ssd); - -+#if defined(CONFIG_METAL) && defined(CONFIG_IOSURFACE) -+typedef void *MTLTexture_id; -+typedef void (*SpiceDisplayMetalCompletion)(void *data); -+ -+SpiceDisplayMetalContext qemu_spice_display_metal_create_context(IOSurfaceRef surface, -+ uint32_t width, -+ uint32_t height); -+void qemu_spice_display_metal_destroy_context(SpiceDisplayMetalContext ctx); -+void qemu_spice_display_metal_scanout_texture(SpiceDisplayMetalContext ctx, -+ MTLTexture_id tex, uint32_t x, uint32_t y, -+ uint32_t w, uint32_t h); -+void qemu_spice_display_metal_scanout_disable(SpiceDisplayMetalContext ctx); -+bool qemu_spice_display_metal_has_scanout(SpiceDisplayMetalContext ctx); -+void qemu_spice_display_metal_draw_frame(SpiceDisplayMetalContext ctx, -+ uint32_t x, uint32_t y, uint32_t w, uint32_t h, -+ SpiceDisplayMetalCompletion completion, -+ void *data); -+#endif -+ - #endif -diff --git a/ui/egl-helpers.c b/ui/egl-helpers.c -index f35223592e..1efac2c994 100644 ---- a/ui/egl-helpers.c -+++ b/ui/egl-helpers.c -@@ -533,7 +533,32 @@ int qemu_egl_init_dpy_cocoa(DisplayGLMode mode) - return -1; - } - -- return qemu_egl_init_dpy(dpy, mode); -+ if (qemu_egl_init_dpy(dpy, mode) < 0) { -+ return -1; -+ } -+ -+#ifdef EGL_METAL_DEVICE_ANGLE -+ if (epoxy_has_egl_extension(qemu_egl_display, "EGL_EXT_device_query")) { -+ EGLDeviceEXT device; -+ void *metal_device; -+ -+ if (!eglQueryDisplayAttribEXT(qemu_egl_display, -+ EGL_DEVICE_EXT, -+ (EGLAttrib *)&device)) { -+ return 0; -+ } -+ -+ if (!eglQueryDeviceAttribEXT(device, -+ EGL_METAL_DEVICE_ANGLE, -+ (EGLAttrib *)&metal_device)) { -+ return 0; -+ } -+ -+ qemu_egl_angle_native_device = metal_device; -+ } -+#endif -+ -+ return 0; - } - - /* ---------------------------------------------------------------------- */ -@@ -625,7 +650,7 @@ int qemu_egl_init_dpy_win32(EGLNativeDisplayType dpy, DisplayGLMode mode) - mode = DISPLAY_GL_MODE_ES; - } - -- if (qemu_egl_init_dpy(dpy, 0, mode) < 0) { -+ if (qemu_egl_init_dpy(dpy, mode) < 0) { - return -1; - } - -diff --git a/ui/meson.build b/ui/meson.build -index 8acb9c0ef0..fffa0da90a 100644 ---- a/ui/meson.build -+++ b/ui/meson.build -@@ -144,6 +144,10 @@ if spice.found() - if quartzcore.found() - spice_core_ss.add(quartzcore) - endif -+ if metal.found() -+ spice_core_ss.add(metal) -+ spice_core_ss.add(files('spice-display-metal.m')) -+ endif - ui_modules += {'spice-core' : spice_core_ss} - - if gio.found() -diff --git a/ui/spice-display-metal.m b/ui/spice-display-metal.m -new file mode 100644 -index 0000000000..29e58d9461 ---- /dev/null -+++ b/ui/spice-display-metal.m -@@ -0,0 +1,166 @@ -+/* -+ * SPDX-License-Identifier: GPL-2.0-or-later -+ * -+ * This work is licensed under the terms of the GNU GPL, version 2 or later. -+ * See the COPYING file in the top-level directory. -+ */ -+ -+#include "qemu/osdep.h" -+#include "ui/egl-helpers.h" -+#include "ui/spice-display.h" -+#include -+#include -+#include -+ -+@interface SpiceDisplayMetal : NSObject -+ -+@property (nonatomic, readonly) id renderTarget; -+@property (nonatomic, readonly) id commandQueue; -+@property (nonatomic, nullable, retain) id scanoutTexture; -+@property (nonatomic, assign) CGRect scanoutRect; -+ -+- (instancetype)initWithDevice:(id)device -+ surface:(IOSurfaceRef)surface -+ width:(NSInteger)width -+ height:(NSInteger)height; -+- (void)scanoutTexture:(id)texture rect:(CGRect)rect; -+- (void)scanoutDisable; -+- (void)drawFrameAtRect:(CGRect)rect completion:(void (^)(void))completion; -+ -+@end -+ -+@implementation SpiceDisplayMetal -+ -+- (instancetype)initWithDevice:(id)device -+ surface:(IOSurfaceRef)surface -+ width:(NSInteger)width -+ height:(NSInteger)height -+{ -+ if (self = [super init]) { -+ MTLTextureDescriptor *textureDescriptor = [[MTLTextureDescriptor alloc] init]; -+ textureDescriptor.pixelFormat = MTLPixelFormatBGRA8Unorm; -+ textureDescriptor.width = width; -+ textureDescriptor.height = height; -+ textureDescriptor.usage = MTLTextureUsageRenderTarget; -+ _renderTarget = [device newTextureWithDescriptor:textureDescriptor iosurface:surface plane:0]; -+ [textureDescriptor release]; -+ if (!_renderTarget) { -+ return nil; -+ } -+ _commandQueue = [device newCommandQueue]; -+ if (!_commandQueue) { -+ [_renderTarget release]; -+ return nil; -+ } -+ } -+ return self; -+} -+ -+- (void)dealloc -+{ -+ [_scanoutTexture release]; -+ [_commandQueue release]; -+ [_renderTarget release]; -+ [super dealloc]; -+} -+ -+- (void)scanoutTexture:(id)texture rect:(CGRect)rect -+{ -+ self.scanoutTexture = texture; -+ self.scanoutRect = rect; -+} -+ -+- (void)scanoutDisable -+{ -+ self.scanoutTexture = nil; -+ self.scanoutRect = CGRectMake(0, 0, 0, 0); -+} -+ -+- (void)drawFrameAtRect:(CGRect)rect completion:(void (^)(void))completion -+{ -+ @autoreleasepool { -+ if (!self.scanoutTexture) { -+ return; -+ } -+ -+ id commandBuffer = [self.commandQueue commandBuffer]; -+ id blit = [commandBuffer blitCommandEncoder]; -+ MTLOrigin origin = MTLOriginMake(self.scanoutRect.origin.x + rect.origin.x, -+ self.scanoutRect.origin.y + rect.origin.y, -+ 0); -+ MTLSize size = MTLSizeMake(rect.size.width, rect.size.height, 1); -+ -+ [blit copyFromTexture:self.scanoutTexture -+ sourceSlice:0 -+ sourceLevel:0 -+ sourceOrigin:origin -+ sourceSize:size -+ toTexture:self.renderTarget -+ destinationSlice:0 -+ destinationLevel:0 -+ destinationOrigin:(MTLOrigin){rect.origin.x,rect.origin.y,0}]; -+ -+ [blit endEncoding]; -+ -+ [commandBuffer addCompletedHandler:^(id cb) { -+ completion(); -+ }]; -+ -+ [commandBuffer commit]; -+ } -+} -+ -+@end -+ -+SpiceDisplayMetalContext qemu_spice_display_metal_create_context(IOSurfaceRef surface, -+ uint32_t width, -+ uint32_t height) -+{ -+ id device = (id)qemu_egl_angle_native_device; -+ -+ if (!device) { -+ return NULL; -+ } -+ -+ return [[SpiceDisplayMetal alloc] initWithDevice:device -+ surface:surface -+ width:width -+ height:height]; -+} -+ -+void qemu_spice_display_metal_destroy_context(SpiceDisplayMetalContext ctx) -+{ -+ [(SpiceDisplayMetal *)ctx release]; -+} -+ -+void qemu_spice_display_metal_scanout_texture(SpiceDisplayMetalContext ctx, -+ MTLTexture_id tex, uint32_t x, uint32_t y, -+ uint32_t w, uint32_t h) -+{ -+ CGRect rect = CGRectMake(x, y, w, h); -+ -+ [(SpiceDisplayMetal *)ctx scanoutTexture:(id)tex -+ rect:rect]; -+} -+ -+void qemu_spice_display_metal_scanout_disable(SpiceDisplayMetalContext ctx) -+{ -+ [(SpiceDisplayMetal *)ctx scanoutDisable]; -+} -+ -+bool qemu_spice_display_metal_has_scanout(SpiceDisplayMetalContext ctx) -+{ -+ return [(SpiceDisplayMetal *)ctx scanoutTexture] != nil; -+} -+ -+void qemu_spice_display_metal_draw_frame(SpiceDisplayMetalContext ctx, -+ uint32_t x, uint32_t y, uint32_t w, uint32_t h, -+ SpiceDisplayMetalCompletion completion, -+ void *data) -+{ -+ CGRect rect = CGRectMake(x, y, w, h); -+ -+ [(SpiceDisplayMetal *)ctx drawFrameAtRect:rect completion:^{ -+ completion(data); -+ }]; -+} -diff --git a/ui/spice-display.c b/ui/spice-display.c -index 97a915a7ee..976f68710a 100644 ---- a/ui/spice-display.c -+++ b/ui/spice-display.c -@@ -939,6 +939,10 @@ static bool spice_iosurface_create(SimpleSpiceDisplay *ssd, int width, int heigh - - ssd->iosurface = surface; - -+#if defined(CONFIG_METAL) -+ ssd->metal_context = qemu_spice_display_metal_create_context(surface, width, height); -+#endif -+ - return true; - } - -@@ -967,6 +971,13 @@ static void spice_iosurface_destroy(SimpleSpiceDisplay *ssd) - return; - } - -+#if defined(CONFIG_METAL) -+ if (ssd->metal_context) { -+ qemu_spice_display_metal_destroy_context(ssd->metal_context); -+ ssd->metal_context = NULL; -+ } -+#endif -+ - if (spice_opengl == DISPLAY_GL_MODE_CORE) { - spice_iosurface_destroy_cgl(ssd); - } else { -@@ -1060,6 +1071,52 @@ static void spice_iosurface_blit(SimpleSpiceDisplay *ssd, GLuint src_texture, bo - } - } - -+#if defined(CONFIG_METAL) -+static void qemu_spice_gl_block(SimpleSpiceDisplay *ssd, bool block); -+ -+static void spice_iosurface_blit_completion(void *data) -+{ -+ QXLCookie *cookie = (QXLCookie *)data; -+ -+ spice_qxl_gl_draw_async(cookie->u.gl_draw.qxl, -+ cookie->u.gl_draw.x, -+ cookie->u.gl_draw.y, -+ cookie->u.gl_draw.w, -+ cookie->u.gl_draw.h, -+ (uintptr_t)cookie); -+} -+ -+static bool spice_iosurface_blit_metal(SimpleSpiceDisplay *ssd, -+ uint32_t x, uint32_t y, uint32_t w, uint32_t h) -+{ -+ SpiceDisplayMetalContext context = ssd->metal_context; -+ QXLCookie *cookie; -+ -+ if (!ssd->iosurface || !context) { -+ return false; -+ } -+ -+ if (!qemu_spice_display_metal_has_scanout(context)) { -+ return false; -+ } -+ -+ cookie = qxl_cookie_new(QXL_COOKIE_TYPE_GL_DRAW_DONE, 0); -+ cookie->u.gl_draw.qxl = &ssd->qxl; -+ cookie->u.gl_draw.x = x; -+ cookie->u.gl_draw.y = y; -+ cookie->u.gl_draw.w = w; -+ cookie->u.gl_draw.h = h; -+ -+ qemu_spice_gl_block(ssd, true); -+ qemu_spice_display_metal_draw_frame(context, -+ x, y, w, h, -+ spice_iosurface_blit_completion, -+ cookie); -+ -+ return true; -+} -+#endif -+ - #endif - - #ifdef HAVE_SPICE_MAC_CGL -@@ -1215,6 +1272,11 @@ static void spice_gl_switch(DisplayChangeListener *dcl, - return; - } - #elif defined(CONFIG_IOSURFACE) -+#if defined(CONFIG_METAL) -+ if (ssd->metal_context) { -+ qemu_spice_display_metal_scanout_disable(ssd->metal_context); -+ } -+#endif - if (spice_iosurface_resize(ssd, width, height)) { - fd = spice_iosurface_create_fd(ssd, &fourcc); - if (fd < 0) { -@@ -1252,6 +1314,8 @@ static QEMUGLContext qemu_spice_gl_create_context(DisplayGLCtx *dgc, - if (spice_opengl == DISPLAY_GL_MODE_CORE) { - #if defined(HAVE_SPICE_MAC_CGL) - return spice_cgl_create_context(spice_gl_ctx); -+#else -+ return NULL; - #endif - } else { - #if defined(CONFIG_GBM) -@@ -1282,6 +1346,8 @@ static int qemu_spice_gl_make_context_current(DisplayGLCtx *dgc, - if (spice_opengl == DISPLAY_GL_MODE_CORE) { - #if defined(HAVE_SPICE_MAC_CGL) - return spice_cgl_make_context_current(ctx); -+#else -+ return -1; - #endif - } else { - return qemu_egl_make_context_current(dgc, ctx); -@@ -1298,6 +1364,11 @@ static void qemu_spice_gl_scanout_disable(DisplayChangeListener *dcl) - ssd->have_surface = false; - ssd->have_scanout = false; - #if defined(CONFIG_IOSURFACE) -+#if defined(CONFIG_METAL) -+ if (ssd->metal_context) { -+ qemu_spice_display_metal_scanout_disable(ssd->metal_context); -+ } -+#endif - spice_iosurface_destroy(ssd); - #endif - ssd->tex_id = -1; -@@ -1326,6 +1397,15 @@ static void qemu_spice_gl_scanout_texture(DisplayChangeListener *dcl, - } else { - fd = -1; - } -+#if defined(CONFIG_METAL) -+ if (ssd->metal_context && native.type == SCANOUT_TEXTURE_NATIVE_TYPE_METAL) { -+ qemu_spice_display_metal_scanout_texture(ssd->metal_context, -+ native.handle, -+ x, y, w, h); -+ } else if (ssd->metal_context) { -+ qemu_spice_display_metal_scanout_disable(ssd->metal_context); -+ } -+#endif - #endif - if (fd < 0) { - fprintf(stderr, "%s: failed to get fd for texture\n", __func__); -@@ -1491,8 +1571,12 @@ static void qemu_spice_gl_update(DisplayChangeListener *dcl, - #elif defined(CONFIG_IOSURFACE) - GLuint tex_id = ssd->tex_id; - y_0_top = ssd->y_0_top; -+#if defined(CONFIG_METAL) -+ if (spice_iosurface_blit_metal(ssd, x, y, w, h)) { -+ return; -+ } -+#endif - spice_iosurface_blit(ssd, tex_id, !y_0_top); -- //TODO: cursor stuff - #endif - - trace_qemu_spice_gl_update(ssd->qxl.id, w, h, x, y); --- -2.41.0 - -From 27e45bc5d22b61a0b1f7094595c8eeb02d1258ec Mon Sep 17 00:00:00 2001 -From: osy -Date: Sat, 10 Jan 2026 10:16:41 -0800 -Subject: [PATCH] hvf: use private APIs to set IPA size when needed - ---- - hw/arm/virt.c | 2 - - target/arm/hvf/hvf.c | 98 ++++++++++++++++++++++++++++++-------------- - target/arm/hvf_arm.h | 2 +- - 3 files changed, 69 insertions(+), 33 deletions(-) - -diff --git a/hw/arm/virt.c b/hw/arm/virt.c -index e2538ae978..3549a91aba 100644 ---- a/hw/arm/virt.c -+++ b/hw/arm/virt.c -@@ -3201,9 +3201,7 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) - mc->valid_cpu_types = valid_cpu_types; - mc->get_default_cpu_node_id = virt_get_default_cpu_node_id; - mc->kvm_type = virt_kvm_type; --#if !defined(CONFIG_HVF_PRIVATE) - mc->hvf_get_physical_address_range = virt_hvf_get_physical_address_range; --#endif - assert(!mc->get_hotplug_handler); - mc->get_hotplug_handler = virt_machine_get_hotplug_handler; - hc->pre_plug = virt_machine_device_pre_plug_cb; -diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c -index 1a0f69b496..230240fec7 100644 ---- a/target/arm/hvf/hvf.c -+++ b/target/arm/hvf/hvf.c -@@ -24,6 +24,7 @@ - #include "cpregs.h" - - #include -+#include - - #include "exec/address-spaces.h" - #include "hw/boards.h" -@@ -977,41 +978,53 @@ static hv_return_t hvf_vcpu_set_actlr(hv_vcpu_t vcpu, uint64_t value) - #endif - } - --#if !defined(CONFIG_HVF_PRIVATE) -- - uint32_t hvf_arm_get_default_ipa_bit_size(void) - { -+#if TARGET_OS_OSX - if (__builtin_available(macOS 13.0, *)) { - uint32_t default_ipa_size; - hv_return_t ret = hv_vm_config_get_default_ipa_size(&default_ipa_size); - assert_hvf_ok(ret); - - return default_ipa_size; -- } else { -- return 0; - } -+#endif -+ return 0; - } - - uint32_t hvf_arm_get_max_ipa_bit_size(void) - { -+ uint64_t ipa_size_4k, ipa_size_16k; -+ size_t length; -+ uint32_t max_ipa_size = 0; -+ -+#if TARGET_OS_OSX - if (__builtin_available(macOS 13.0, *)) { -- uint32_t max_ipa_size; - hv_return_t ret = hv_vm_config_get_max_ipa_size(&max_ipa_size); - assert_hvf_ok(ret); -+ } -+#endif - -- /* -- * We clamp any IPA size we want to back the VM with to a valid PARange -- * value so the guest doesn't try and map memory outside of the valid -- * range. This logic just clamps the passed in IPA bit size to the first -- * valid PARange value <= to it. -- */ -- return round_down_to_parange_bit_size(max_ipa_size); -- } else { -- return 0; -+ if (!max_ipa_size) { -+ length = sizeof(uint64_t); -+ if (sysctlbyname("kern.hv.ipa_size_16k", &ipa_size_16k, &length, NULL, 0)) { -+ ipa_size_16k = 0; -+ } -+ length = sizeof(uint64_t); -+ if (sysctlbyname("kern.hv.ipa_size_4k", &ipa_size_4k, &length, NULL, 0)) { -+ ipa_size_4k = 0; -+ } -+ max_ipa_size = MIN(ctz64(ipa_size_16k), ctz64(ipa_size_4k)); - } --} - --#endif -+ /* -+ * We clamp any IPA size we want to back the VM with to a valid PARange -+ * value so the guest doesn't try and map memory outside of the valid -+ * range. This logic just clamps the passed in IPA bit size to the first -+ * valid PARange value <= to it. -+ */ -+ return round_down_to_parange_bit_size(max_ipa_size); -+} - - void hvf_arm_set_cpu_features_from_host(ARMCPU *cpu) - { -@@ -1075,27 +1088,54 @@ static hv_return_t hvf_set_ipa_granule(hv_vm_config_t config, - return HV_SUCCESS; - } - -+static hv_return_t hvf_set_ipa_size(hv_vm_config_t config, uint32_t pa_range) -+{ -+ static hv_return_t (*set_ipa_size)(hv_vm_config_t, uint64_t); -+ hv_return_t ret; -+ -+#if TARGET_OS_OSX -+ if (__builtin_available(macOS 13.0, *)) { -+ ret = hv_vm_config_set_ipa_size(config, pa_range); -+ if (ret == HV_SUCCESS) { -+ chosen_ipa_bit_size = pa_range; -+ } -+ return ret; -+ } -+#endif -+ -+ /* older macOS need to use a private API */ -+ if (!set_ipa_size) { -+ set_ipa_size = dlsym(RTLD_NEXT, "_hv_vm_config_set_ipa_size"); -+ } -+ if (set_ipa_size) { -+ ret = set_ipa_size(config, 1ULL << pa_range); -+ if (ret == HV_SUCCESS) { -+ chosen_ipa_bit_size = pa_range; -+ } -+ return ret; -+ } else if (!pa_range) { -+ return HV_SUCCESS; -+ } -+ -+ return HV_UNSUPPORTED; -+} -+ - hv_return_t hvf_arch_vm_create(MachineState *ms, uint32_t pa_range, - uint32_t ipa_granule_size) - { - hv_return_t ret; -- hv_vm_config_t config = NULL; -+ hv_vm_config_t config = hv_vm_config_create(); - - #if defined(CONFIG_HVF_PRIVATE) - if (hvf_tso_mode) { -- config = hv_vm_config_create(); - _hv_vm_config_set_isa(config, HV_VM_CONFIG_ISA_PRIVATE); - } --#else -- if (__builtin_available(macOS 13.0, *)) { -- config = hv_vm_config_create(); -- ret = hv_vm_config_set_ipa_size(config, pa_range); -- if (ret != HV_SUCCESS) { -- goto cleanup; -- } -- chosen_ipa_bit_size = pa_range; -- } - #endif -+ -+ ret = hvf_set_ipa_size(config, pa_range); -+ if (ret != HV_SUCCESS) { -+ goto cleanup; -+ } - - if (ipa_granule_size) { - ret = hvf_set_ipa_granule(config, ipa_granule_size); -@@ -1107,9 +1147,7 @@ hv_return_t hvf_arch_vm_create(MachineState *ms, uint32_t pa_range, - ret = hv_vm_create(config); - - cleanup: -- if (config) { -- os_release(config); -- } -+ os_release(config); - - return ret; - } -diff --git a/target/arm/hvf_arm.h b/target/arm/hvf_arm.h -index 482768baa6..13bc8921b3 100644 ---- a/target/arm/hvf_arm.h -+++ b/target/arm/hvf_arm.h -@@ -22,7 +22,7 @@ void hvf_arm_init_debug(void); - - void hvf_arm_set_cpu_features_from_host(ARMCPU *cpu); - --#if defined(CONFIG_HVF) && !defined(CONFIG_HVF_PRIVATE) -+#if defined(CONFIG_HVF) - - uint32_t hvf_arm_get_default_ipa_bit_size(void); - uint32_t hvf_arm_get_max_ipa_bit_size(void); --- -2.41.0 - diff --git a/patches/sources b/patches/sources index d6f9a376..e7b46267 100644 --- a/patches/sources +++ b/patches/sources @@ -23,7 +23,7 @@ SPICE_SERVER_SRC="https://www.spice-space.org/download/releases/spice-server/spi USB_SRC="https://github.com/libusb/libusb/releases/download/v1.0.25/libusb-1.0.25.tar.bz2" USBREDIR_SRC="https://www.spice-space.org/download/usbredir/usbredir-0.14.0.tar.xz" SLIRP_SRC="https://github.com/utmapp/libslirp/releases/download/v4.9.1-release-mirror/libslirp-v4.9.1.tar.gz" -QEMU_SRC="https://github.com/utmapp/qemu/releases/download/v10.0.2-utm/qemu-10.0.2-utm.tar.xz" +QEMU_SRC="https://github.com/utmapp/qemu/releases/download/v10.0.12-utm/qemu-10.0.12-utm.tar.xz" # Source files for spice-client JSON_GLIB_SRC="https://download.gnome.org/sources/json-glib/1.10/json-glib-1.10.0.tar.xz" @@ -42,15 +42,20 @@ WEBKIT_REPO="https://github.com/utmapp/WebKit.git" WEBKIT_COMMIT="ed78ab6e1a37f4f11583a0bd038f22ec91f3ff10" WEBKIT_SUBDIRS="Source/ThirdParty/ANGLE Configurations Tools/ccache" EPOXY_REPO="https://github.com/utmapp/libepoxy.git" -EPOXY_COMMIT="5014658f79e4d6872a1ad6754da9098ccd9d4fc5" +EPOXY_COMMIT="bf98587477fe68d07b93319ece7b40a7d0e2eabe" VULKAN_LOADER_REPO="https://github.com/osy/Vulkan-Loader.git" VULKAN_LOADER_COMMIT="6df86907be582927507637f3f6003ea58bc34517" VIRGLRENDERER_REPO="https://github.com/utmapp/virglrenderer.git" -VIRGLRENDERER_COMMIT="d48a2d0d9a722fffd3f92c83e71d9426a4892a66" +VIRGLRENDERER_COMMIT="9499d3e53edbf356fb3e8f5ab68b27e208003022" MESA_REPO="https://gitlab.freedesktop.org/osy/mesa.git" MESA_COMMIT="79bc850d884a1307356ff61c017e58901b90c7e2" MOLTENVK_REPO="https://github.com/utmapp/MoltenVK.git" -MOLTENVK_COMMIT="111c14f3abf5c00118fc7a5b00c92d7abbf40f62" +MOLTENVK_COMMIT="ef1c5461774f5fbd224ddcfd91fd2c0ea23f0384" +LLVM15_SRC="https://github.com/llvm/llvm-project/releases/download/llvmorg-15.0.7/llvm-project-15.0.7.src.tar.xz" +DXMT_REPO="https://github.com/utmapp/dxmt.git" +DXMT_COMMIT="be80e14c07613cd558036a576b82dc6d78acdfce" +D3DMETAL_REPO="https://github.com/utmapp/d3dmetal-native.git" +D3DMETAL_COMMIT="edd2e386c25a2039eca0f6ce8f794b3b165841eb" # Decompiled Hypervisor for iOS HYPERVISOR_REPO="https://github.com/utmapp/Hypervisor.git" diff --git a/scripts/build_dependencies.sh b/scripts/build_dependencies.sh index ff291778..2cfedfbf 100755 --- a/scripts/build_dependencies.sh +++ b/scripts/build_dependencies.sh @@ -21,9 +21,13 @@ RED='\033[0;31m' NC='\033[0m' # Knobs -IOS_SDKMINVER="14.0" -MAC_SDKMINVER="11.0" +IOS_SDKMINVER="16.0" +MAC_SDKMINVER="13.0" VISIONOS_SDKMINVER="1.0" +# Network operations are retried because concurrent builds on the same CI runner +# get rate limited by GitHub and fail to clone/fetch. +RETRY_ATTEMPTS="${RETRY_ATTEMPTS:-5}" +RETRY_DELAY="${RETRY_DELAY:-15}" # Build environment PLATFORM= @@ -90,6 +94,26 @@ check_env () { version_check "2.4" "$(bison -V | head -1 | awk '{ print $NF }')" || { echo >&2 "${RED}'bison' >= 2.4 is required. Did you install from Homebrew and updated your \$PATH variable?"; exit 1; } } +# Run a command, retrying with exponential backoff if it fails. Jitter is derived +# from the PID so that concurrent builds do not retry in lockstep. +retry () { + ATTEMPT=1 + DELAY="$RETRY_DELAY" + while true; do + "$@" && return 0 + STATUS=$? + if [ $ATTEMPT -ge $RETRY_ATTEMPTS ]; then + echo >&2 "${RED}'$*' failed after $ATTEMPT attempts.${NC}" + return $STATUS + fi + SLEEP=$(( DELAY + ($$ + ATTEMPT) % 10 )) + echo >&2 "${RED}'$*' failed (attempt $ATTEMPT of $RETRY_ATTEMPTS), retrying in ${SLEEP}s...${NC}" + sleep $SLEEP + ATTEMPT=$(( ATTEMPT + 1 )) + DELAY=$(( DELAY * 2 )) + done +} + download () { URL=$1 FILE="$(basename $URL)" @@ -102,7 +126,7 @@ download () { echo "${GREEN}$TARGET already downloaded! Run with -d to force re-download.${NC}" else echo "${GREEN}Downloading ${URL}${NC}" - curl -L -O "$URL" + retry curl -f -L -O "$URL" mv "$FILE" "$TARGET" fi if [ -d "$DIR" ]; then @@ -121,6 +145,12 @@ download () { fi } +# Discard any partial clone from a previous attempt so that a retry starts clean. +clone_fresh () { + rm -rf "$2" + git clone --filter=tree:0 --no-checkout "$1" "$2" +} + clone () { REPO="$1" COMMIT="$2" @@ -130,15 +160,26 @@ clone () { if [ -d "$DIR" -a -z "$REDOWNLOAD" ]; then echo "${GREEN}$DIR already downloaded! Run with -d to force re-download.${NC}" else - rm -rf "$DIR" echo "${GREEN}Cloning ${REPO}...${NC}" - git clone --filter=tree:0 --no-checkout "$REPO" "$DIR" + retry clone_fresh "$REPO" "$DIR" if [ ! -z "$SUBDIRS" ]; then git -C "$DIR" sparse-checkout init git -C "$DIR" sparse-checkout set $SUBDIRS fi fi - git -C "$DIR" checkout "$COMMIT" + # tree:0 filtered clones fetch missing objects here, so these hit the network too + retry git -C "$DIR" checkout "$COMMIT" + retry git -C "$DIR" submodule update --init --recursive --filter=tree:0 +} + +clone_moltenvk_dependences () { + REPO="$1" + NAME="$(basename $REPO)" + DIR="$BUILD_DIR/$NAME" + pwd="$(pwd)" + cd "$DIR" + retry "./fetchDependencies" --none -v + cd "$pwd" } download_all () { @@ -182,6 +223,10 @@ download_all () { clone $LIBUCONTEXT_REPO $LIBUCONTEXT_COMMIT clone $MESA_REPO $MESA_COMMIT clone $MOLTENVK_REPO $MOLTENVK_COMMIT + clone_moltenvk_dependences $MOLTENVK_REPO + download $LLVM15_SRC + clone $DXMT_REPO $DXMT_COMMIT + clone $D3DMETAL_REPO $D3DMETAL_COMMIT } copy_private_headers() { @@ -225,13 +270,16 @@ generate_meson_cross() { echo "c_args = [${CFLAGS:+$(meson_quote $CFLAGS)}]" >> $cross echo "cpp_args = [${CXXFLAGS:+$(meson_quote $CXXFLAGS)}]" >> $cross echo "objc_args = [${OBJCFLAGS:+$(meson_quote $OBJCFLAGS)}]" >> $cross + echo "objcpp_args = [${OBJCFLAGS:+$(meson_quote $OBJCFLAGS)}]" >> $cross echo "c_link_args = [${LDFLAGS:+$(meson_quote $LDFLAGS)}]" >> $cross echo "cpp_link_args = [${LDFLAGS:+$(meson_quote $LDFLAGS)}]" >> $cross echo "objc_link_args = [${LDFLAGS:+$(meson_quote $LDFLAGS)}]" >> $cross + echo "objcpp_link_args = [${LDFLAGS:+$(meson_quote $LDFLAGS)}]" >> $cross echo "[binaries]" >> $cross echo "c = [$(meson_quote $CC)]" >> $cross echo "cpp = [$(meson_quote $CXX)]" >> $cross echo "objc = [$(meson_quote $OBJCC)]" >> $cross + echo "objcpp = [$(meson_quote $OBJCC)]" >> $cross echo "ar = [$(meson_quote $AR)]" >> $cross echo "nm = [$(meson_quote $NM)]" >> $cross echo "pkgconfig = ['$PREFIX/host/bin/pkg-config']" >> $cross @@ -293,19 +341,19 @@ generate_cmake_toolchain() { case $PLATFORM in ios_simulator* ) echo "set(CMAKE_SYSTEM_NAME iOS)" >> "$toolchain" - echo "set(CMAKE_OSX_SYSROOT iphonesimulator)" >> "$toolchain" + echo "set(CMAKE_OSX_SYSROOT iphonesimulator CACHE STRING \"\" FORCE)" >> "$toolchain" ;; ios* ) echo "set(CMAKE_SYSTEM_NAME iOS)" >> "$toolchain" - echo "set(CMAKE_OSX_SYSROOT iphoneos)" >> "$toolchain" + echo "set(CMAKE_OSX_SYSROOT iphoneos CACHE STRING \"\" FORCE)" >> "$toolchain" ;; visionos_simulator* ) echo "set(CMAKE_SYSTEM_NAME visionOS)" >> "$toolchain" - echo "set(CMAKE_OSX_SYSROOT xrsimulator)" >> "$toolchain" + echo "set(CMAKE_OSX_SYSROOT xrsimulator CACHE STRING \"\" FORCE)" >> "$toolchain" ;; visionos* ) echo "set(CMAKE_SYSTEM_NAME visionOS)" >> "$toolchain" - echo "set(CMAKE_OSX_SYSROOT xros)" >> "$toolchain" + echo "set(CMAKE_OSX_SYSROOT xros CACHE STRING \"\" FORCE)" >> "$toolchain" ;; macos ) echo "set(CMAKE_SYSTEM_NAME Darwin)" >> "$toolchain" @@ -317,7 +365,7 @@ generate_cmake_toolchain() { # Architecture # echo "set(CMAKE_SYSTEM_PROCESSOR \"$ARCH\")" >> "$toolchain" - echo "set(CMAKE_OSX_ARCHITECTURES \"$ARCH\")" >> "$toolchain" + echo "set(CMAKE_OSX_ARCHITECTURES \"$ARCH\" CACHE STRING \"\" FORCE)" >> "$toolchain" echo "set(CMAKE_MACOSX_BUNDLE OFF CACHE BOOL \"\")" >> "$toolchain" echo "" >> "$toolchain" @@ -343,6 +391,7 @@ generate_cmake_toolchain() { echo "set(CMAKE_C_COMPILER \"$CC_BIN\")" >> "$toolchain" echo "set(CMAKE_CXX_COMPILER \"$CXX_BIN\")" >> "$toolchain" echo "set(CMAKE_OBJC_COMPILER \"$OBJC_BIN\")" >> "$toolchain" + echo "set(CMAKE_ASM_COMPILER \"$CC_BIN\")" >> "$toolchain" echo "" >> "$toolchain" # @@ -359,6 +408,7 @@ generate_cmake_toolchain() { # if [ -n "$CFLAGS" ]; then echo "set(CMAKE_C_FLAGS \"$CFLAGS\" CACHE STRING \"\" FORCE)" >> "$toolchain" + echo "set(CMAKE_ASM_FLAGS \"$CFLAGS\" CACHE STRING \"\" FORCE)" >> "$toolchain" fi if [ -n "$OBJCFLAGS" ]; then @@ -695,7 +745,12 @@ build_qemu_dependencies () { # strip the minor versions VULKAN_DYLIB="$PREFIX/lib/libvulkan.1.dylib" mv "$(dirname $VULKAN_DYLIB)/$(readlink $VULKAN_DYLIB)" "$VULKAN_DYLIB" - meson_darwin_build $VIRGLRENDERER_REPO -Dtests=false -Dcheck-gl-errors=false -Dvenus=true -Dvulkan-dload=false -Drender-server-worker=thread + if [ "$PLATFORM" == "macos" ]; then + VIRGL_RENDER_SERVER_MODE="process" + else + VIRGL_RENDER_SERVER_MODE="thread" + fi + meson_darwin_build $VIRGLRENDERER_REPO -Dtests=false -Dcheck-gl-errors=false -Dvenus=true -Dneptune=true -Dvulkan-dload=false -Drender-server-mode="$VIRGL_RENDER_SERVER_MODE" # Hypervisor for iOS if [ "$PLATFORM" == "ios" ] || [ "$PLATFORM" == "ios_simulator" ]; then build_hypervisor @@ -785,7 +840,39 @@ build_vulkan_drivers () { patch_vulkan_icd "$PREFIX/share/vulkan/icd.d/MoltenVK_icd.json" } -fixup () { +build_d3d_drivers () { + LLVM15_NAME=$(basename "${LLVM15_SRC%.tar.*}") + cmake_build "$BUILD_DIR/$LLVM15_NAME/llvm" -DLLVM_ENABLE_ZSTD=Off -DLLVM_TARGETS_TO_BUILD="" -DLLVM_BUILD_TOOLS=Off -DLLVM_VERSION_PRINTER_SHOW_HOST_TARGET_INFO=Off + meson_build $DXMT_REPO -Dnative_llvm_path="$PREFIX" + if [ "$ARCH" == "x86_64" ]; then + meson_build $D3DMETAL_REPO -Dtests=disabled + else + # empty placeholder file + $CC -dynamiclib -x c /dev/null -o "$PREFIX/lib/libd3dmetal-native.dylib" + fi +} + +fixup_imports() { + _file=$1 + _list=$(otool -L "$_file" | tail -n +2 | cut -d ' ' -f 1 | awk '{$1=$1};1') + for g in $_list + do + base=$(basename "$g") + basefilename=${base%.*} + libname=${basefilename#lib*} + dir=$(dirname "$g") + if [ "$dir" == "$PREFIX/lib" ] || [ "$dir" == "@rpath" ]; then + if [ "$PLATFORM" == "macos" ]; then + newname="@rpath/$libname.framework/Versions/A/$libname" + else + newname="@rpath/$libname.framework/$libname" + fi + install_name_tool -change "$g" "$newname" "$_file" + fi + done +} + +fixup_dylib () { FILE=$1 BASE=$(basename "$FILE") BASEFILENAME=${BASE%.*} @@ -801,18 +888,16 @@ fixup () { INFOPATH="$FRAMEWORKPATH" fi NEWFILE="$FRAMEWORKPATH/$LIBNAME" - LIST=$(otool -L "$FILE" | tail -n +2 | cut -d ' ' -f 1 | awk '{$1=$1};1') OLDIFS=$IFS IFS=$'\n' - echo "${GREEN}Fixing up $FILE...${NC}" mkdir -p "$FRAMEWORKPATH" mkdir -p "$INFOPATH" cp -a "$FILE" "$NEWFILE" - /usr/libexec/PlistBuddy -c "Add :CFBundleExecutable string $LIBNAME" "$INFOPATH/Info.plist" - /usr/libexec/PlistBuddy -c "Add :CFBundleIdentifier string $BUNDLE_ID" "$INFOPATH/Info.plist" - /usr/libexec/PlistBuddy -c "Add :MinimumOSVersion string $SDKMINVER" "$INFOPATH/Info.plist" - /usr/libexec/PlistBuddy -c "Add :CFBundleVersion string 1" "$INFOPATH/Info.plist" - /usr/libexec/PlistBuddy -c "Add :CFBundleShortVersionString string 1.0" "$INFOPATH/Info.plist" + /usr/libexec/PlistBuddy -c "Add :CFBundleExecutable string $LIBNAME" "$INFOPATH/Info.plist" || true + /usr/libexec/PlistBuddy -c "Add :CFBundleIdentifier string $BUNDLE_ID" "$INFOPATH/Info.plist" || true + /usr/libexec/PlistBuddy -c "Add :MinimumOSVersion string $SDKMINVER" "$INFOPATH/Info.plist" || true + /usr/libexec/PlistBuddy -c "Add :CFBundleVersion string 1" "$INFOPATH/Info.plist" || true + /usr/libexec/PlistBuddy -c "Add :CFBundleShortVersionString string 1.0" "$INFOPATH/Info.plist" || true if [ "$PLATFORM" == "macos" ]; then ln -sf "A" "$BASEFRAMEWORKPATH/Versions/Current" ln -sf "Versions/Current/Resources" "$BASEFRAMEWORKPATH/Resources" @@ -820,21 +905,7 @@ fixup () { fi newname="@rpath/$FRAMEWORKNAME/$LIBNAME" install_name_tool -id "$newname" "$NEWFILE" - for g in $LIST - do - base=$(basename "$g") - basefilename=${base%.*} - libname=${basefilename#lib*} - dir=$(dirname "$g") - if [ "$dir" == "$PREFIX/lib" ] || [ "$dir" == "@rpath" ]; then - if [ "$PLATFORM" == "macos" ]; then - newname="@rpath/$libname.framework/Versions/A/$libname" - else - newname="@rpath/$libname.framework/$libname" - fi - install_name_tool -change "$g" "$newname" "$NEWFILE" - fi - done + fixup_imports "$NEWFILE" IFS=$OLDIFS } @@ -844,7 +915,14 @@ fixup_all () { FILES=$(find "$SYSROOT_DIR/lib" -type f -maxdepth 1 -name "*.dylib") for f in $FILES do - fixup $f + echo "${GREEN}Fixing up $f...${NC}" + fixup_dylib $f + done + FILES=$(find "$SYSROOT_DIR/libexec" -type f -maxdepth 1 -perm +111) + for f in $FILES + do + echo "${GREEN}Fixing up $f...${NC}" + fixup_imports $f done IFS=$OLDIFS } @@ -1089,7 +1167,7 @@ fi echo "${GREEN}Deleting old sysroot!${NC}" rm -rf "$PREFIX/"* rm -f "$BUILD_DIR/BUILD_SUCCESS" -rm -f "$BUILD_DIR/meson*.cross" +rm -f "$BUILD_DIR"/meson*.cross rm -f "$BUILD_DIR/cross.cmake" mkdir -p "$PREFIX/Frameworks" copy_private_headers @@ -1098,6 +1176,9 @@ build_qemu_dependencies build $QEMU_DIR --cross-prefix="" $QEMU_PLATFORM_BUILD_FLAGS $QEMU_DEBUG_FLAGS build_spice_client build_vulkan_drivers +if [ "$PLATFORM" == "macos" ]; then + build_d3d_drivers +fi fixup_all remove_shared_gst_plugins # another hack... echo "${GREEN}All done!${NC}" diff --git a/scripts/build_utm.sh b/scripts/build_utm.sh index 155eaa14..8430795d 100755 --- a/scripts/build_utm.sh +++ b/scripts/build_utm.sh @@ -92,10 +92,12 @@ if [ "$SDK" == "macosx" ]; then # this way we can import into Xcode and re-sign from there UTM_ENTITLEMENTS="/tmp/utm.$$.entitlements" LAUNCHER_ENTITLEMENTS="/tmp/launcher.$$.entitlements" + RENDERER_ENTITLEMENTS="/tmp/renderer.$$.entitlements" HELPER_ENTITLEMENTS="/tmp/helper.$$.entitlements" CLI_ENTITLEMENTS="/tmp/cli.$$.entitlements" cp "$BASEDIR/../Platform/macOS/macOS.entitlements" "$UTM_ENTITLEMENTS" cp "$BASEDIR/../QEMULauncher/QEMULauncher.entitlements" "$LAUNCHER_ENTITLEMENTS" + cp "$BASEDIR/../QEMURenderServer/QEMURenderServer.entitlements" "$RENDERER_ENTITLEMENTS" cp "$BASEDIR/../QEMUHelper/QEMUHelper.entitlements" "$HELPER_ENTITLEMENTS" cp "$BASEDIR/../utmctl/utmctl.entitlements" "$CLI_ENTITLEMENTS" if [ ! -z "$TEAM_IDENTIFIER" ]; then @@ -105,12 +107,15 @@ if [ "$SDK" == "macosx" ]; then /usr/libexec/PlistBuddy -c "Set :com.apple.security.application-groups:0 ${TEAM_ID_PREFIX}${PRODUCT_BUNDLE_PREFIX}.UTM" "$UTM_ENTITLEMENTS" /usr/libexec/PlistBuddy -c "Set :com.apple.security.application-groups:0 ${TEAM_ID_PREFIX}${PRODUCT_BUNDLE_PREFIX}.UTM" "$HELPER_ENTITLEMENTS" /usr/libexec/PlistBuddy -c "Set :com.apple.security.application-groups:0 ${TEAM_ID_PREFIX}${PRODUCT_BUNDLE_PREFIX}.UTM" "$CLI_ENTITLEMENTS" + # nested bundles must be signed before the XPC service that contains them codesign --force --sign - --entitlements "$LAUNCHER_ENTITLEMENTS" --timestamp=none --options runtime "$BUILT_PATH/Contents/XPCServices/QEMUHelper.xpc/Contents/MacOS/QEMULauncher.app/Contents/MacOS/QEMULauncher" + codesign --force --sign - --entitlements "$RENDERER_ENTITLEMENTS" --timestamp=none --options runtime "$BUILT_PATH/Contents/XPCServices/QEMUHelper.xpc/Contents/MacOS/QEMURenderServer.app/Contents/MacOS/QEMURenderServer" codesign --force --sign - --entitlements "$HELPER_ENTITLEMENTS" --timestamp=none --options runtime "$BUILT_PATH/Contents/XPCServices/QEMUHelper.xpc/Contents/MacOS/QEMUHelper" codesign --force --sign - --entitlements "$CLI_ENTITLEMENTS" --timestamp=none --options runtime "$BUILT_PATH/Contents/MacOS/utmctl" codesign --force --sign - --entitlements "$UTM_ENTITLEMENTS" --timestamp=none --options runtime "$BUILT_PATH/Contents/MacOS/UTM" rm "$UTM_ENTITLEMENTS" rm "$LAUNCHER_ENTITLEMENTS" + rm "$RENDERER_ENTITLEMENTS" rm "$HELPER_ENTITLEMENTS" rm "$CLI_ENTITLEMENTS" fi diff --git a/scripts/install_d3dmetal.sh b/scripts/install_d3dmetal.sh new file mode 100755 index 00000000..7602ffa9 --- /dev/null +++ b/scripts/install_d3dmetal.sh @@ -0,0 +1,80 @@ +#!/bin/sh +set -e + +usage () { + echo "Usage: $(basename $0) sysroot-dir [dmg-url]" + echo "" + echo " sysroot-dir Sysroot to install into, e.g. sysroot-macOS-arm64_x86_64." + echo " dmg-url D3DMetal distribution DMG, either a URL or a local path." + echo "" + echo "Installs D3DMetal.framework into /Frameworks, rebranded to" + echo "BUNDLE_ID below and ad-hoc signed. Does nothing if no URL is given." + echo "" + exit 1 +} + +# D3DMetal loads its own default.metallib through +# [NSBundle bundleWithIdentifier:@"com.apple.D3DMetal"] +# using that identifier as a literal compiled into the framework binary. We must +# not ship a com.apple.* bundle, so the identifier is rewritten in BOTH the +# Info.plist and the binary -- rewriting only one makes the lookup return nil and +# every D3DMetal process aborts in -[MTLDevice newLibraryWithFile:nil error:]. +APPLE_BUNDLE_ID=com.apple.D3DMetal +BUNDLE_ID=com.elppa.D3DMetal + +if [ $# -lt 1 ]; then + usage +fi + +SYSROOT_DIR="$1" +DMG_URL="$2" + +if [ -z "$DMG_URL" ]; then + echo "note: no D3DMetal DMG URL given, skipping" + exit 0 +fi + +if [ ${#APPLE_BUNDLE_ID} -ne ${#BUNDLE_ID} ]; then + echo "error: $BUNDLE_ID must be ${#APPLE_BUNDLE_ID} bytes to patch in place" >&2 + exit 1 +fi + +FRAMEWORK="$SYSROOT_DIR/Frameworks/D3DMetal.framework" +MOUNT="$PWD/d3dmetal-mount" +DMG="$PWD/d3dmetal.dmg" + +# Rewrite every occurrence of $1 with $2 in the binary $3, in place. Fails if the +# identifier is absent, so a future D3DMetal that drops it cannot ship silently. +patch_binary_identifier () { + LC_ALL=C perl -0777 -i -pe ' + BEGIN { $old = shift @ARGV; $new = shift @ARGV } + $found += s/\Q$old\E/$new/g; + END { die "error: $old not found in $ARGV\n" unless $found } + ' "$1" "$2" "$3" +} + +if [ -f "$DMG_URL" ]; then + DMG="$DMG_URL" +else + curl -f -L -o "$DMG" "$DMG_URL" +fi + +# The distribution DMG carries a license agreement, which hdiutil prompts for on +# stdin unless we answer it. +mkdir -p "$MOUNT" +yes | PAGER=cat hdiutil attach -nobrowse -readonly -noautoopen -mountpoint "$MOUNT" "$DMG" +trap 'hdiutil detach "$MOUNT" -quiet || true' EXIT + +mkdir -p "$SYSROOT_DIR/Frameworks" +rm -rf "$FRAMEWORK" +ditto "$MOUNT/redist/lib/external/D3DMetal.framework" "$FRAMEWORK" +xattr -drs com.apple.quarantine "$FRAMEWORK" + +patch_binary_identifier "$APPLE_BUNDLE_ID" "$BUNDLE_ID" "$FRAMEWORK/Versions/A/D3DMetal" +plutil -replace CFBundleIdentifier -string "$BUNDLE_ID" "$FRAMEWORK/Resources/Info.plist" + +# Signs after the rewrite so the signing identifier is derived from the new +# Info.plist and the two agree. +codesign --force --sign - "$FRAMEWORK" + +echo "installed D3DMetal $(plutil -extract CFBundleShortVersionString raw "$FRAMEWORK/Resources/version.plist") as $BUNDLE_ID into $FRAMEWORK" diff --git a/scripts/pack_dependencies.sh b/scripts/pack_dependencies.sh index 550040a8..135d0e35 100755 --- a/scripts/pack_dependencies.sh +++ b/scripts/pack_dependencies.sh @@ -72,6 +72,7 @@ pack_dir() { cp -a "$SRC/$DIR" "$TGT/$DIR" } +pack_all_objs "$BASEDIR" "*/libexec/virgl_render_server" pack_all_objs "$BASEDIR" "*/bin/qemu-*" pack_all_objs "$BASEDIR" "*/lib/*.dylib" pack_all_objs "$BASEDIR" "*/lib/*.a" diff --git a/scripts/package_mac.sh b/scripts/package_mac.sh index 7a68b243..160dd404 100755 --- a/scripts/package_mac.sh +++ b/scripts/package_mac.sh @@ -31,6 +31,7 @@ OPTIONS="/tmp/options.$$.plist" SIGNED="/tmp/signed.$$" UTM_ENTITLEMENTS="/tmp/utm.$$.entitlements" LAUNCHER_ENTITLEMENTS="/tmp/launcher.$$.entitlements" +RENDERER_ENTITLEMENTS="/tmp/renderer.$$.entitlements" HELPER_ENTITLEMENTS="/tmp/helper.$$.entitlements" CLI_ENTITLEMENTS="/tmp/cli.$$.entitlements" INPUT_COPY="/tmp/UTM.$$.xcarchive" @@ -71,11 +72,13 @@ EOL if [ "$MODE" == "unsigned" ]; then cp "$BASEDIR/../Platform/macOS/macOS-unsigned.entitlements" "$UTM_ENTITLEMENTS" cp "$BASEDIR/../QEMULauncher/QEMULauncher-unsigned.entitlements" "$LAUNCHER_ENTITLEMENTS" + cp "$BASEDIR/../QEMURenderServer/QEMURenderServer-unsigned.entitlements" "$RENDERER_ENTITLEMENTS" cp "$BASEDIR/../QEMUHelper/QEMUHelper-unsigned.entitlements" "$HELPER_ENTITLEMENTS" cp "$BASEDIR/../utmctl/utmctl-unsigned.entitlements" "$CLI_ENTITLEMENTS" else cp "$BASEDIR/../Platform/macOS/macOS.entitlements" "$UTM_ENTITLEMENTS" cp "$BASEDIR/../QEMULauncher/QEMULauncher.entitlements" "$LAUNCHER_ENTITLEMENTS" + cp "$BASEDIR/../QEMURenderServer/QEMURenderServer.entitlements" "$RENDERER_ENTITLEMENTS" cp "$BASEDIR/../QEMUHelper/QEMUHelper.entitlements" "$HELPER_ENTITLEMENTS" cp "$BASEDIR/../utmctl/utmctl.entitlements" "$CLI_ENTITLEMENTS" @@ -92,7 +95,9 @@ fi rm -rf "$INPUT_COPY" cp -a "$INPUT" "$INPUT_COPY" find "$INPUT_COPY/Products/Applications/UTM.app" -type d -path '*/Frameworks/*.framework' -exec codesign --force --sign - --timestamp=none \{\} \; +# nested bundles must be signed before the XPC service that contains them codesign --force --sign - --entitlements "$LAUNCHER_ENTITLEMENTS" --timestamp=none --options runtime "$INPUT_COPY/Products/Applications/UTM.app/Contents/XPCServices/QEMUHelper.xpc/Contents/MacOS/QEMULauncher.app/Contents/MacOS/QEMULauncher" +codesign --force --sign - --entitlements "$RENDERER_ENTITLEMENTS" --timestamp=none --options runtime "$INPUT_COPY/Products/Applications/UTM.app/Contents/XPCServices/QEMUHelper.xpc/Contents/MacOS/QEMURenderServer.app/Contents/MacOS/QEMURenderServer" codesign --force --sign - --entitlements "$HELPER_ENTITLEMENTS" --timestamp=none --options runtime "$INPUT_COPY/Products/Applications/UTM.app/Contents/XPCServices/QEMUHelper.xpc/Contents/MacOS/QEMUHelper" codesign --force --sign - --entitlements "$CLI_ENTITLEMENTS" --timestamp=none --options runtime "$INPUT_COPY/Products/Applications/UTM.app/Contents/MacOS/utmctl" codesign --force --sign - --entitlements "$UTM_ENTITLEMENTS" --timestamp=none --options runtime "$INPUT_COPY/Products/Applications/UTM.app/Contents/MacOS/UTM" @@ -107,6 +112,7 @@ fi rm "$OPTIONS" rm "$UTM_ENTITLEMENTS" rm "$LAUNCHER_ENTITLEMENTS" +rm "$RENDERER_ENTITLEMENTS" rm "$HELPER_ENTITLEMENTS" rm "$CLI_ENTITLEMENTS" rm -rf "$INPUT_COPY"