diff --git a/MeloNX-XC/MeloNX.xcodeproj/project.xcworkspace/xcuserdata/stossy11.xcuserdatad/UserInterfaceState.xcuserstate b/MeloNX-XC/MeloNX.xcodeproj/project.xcworkspace/xcuserdata/stossy11.xcuserdatad/UserInterfaceState.xcuserstate index 1eb47e136..7e64cfd45 100644 Binary files a/MeloNX-XC/MeloNX.xcodeproj/project.xcworkspace/xcuserdata/stossy11.xcuserdatad/UserInterfaceState.xcuserstate and b/MeloNX-XC/MeloNX.xcodeproj/project.xcworkspace/xcuserdata/stossy11.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/MeloNX-XC/MeloNX/ContentView.swift b/MeloNX-XC/MeloNX/ContentView.swift index 689fbe450..f77fe97bb 100644 --- a/MeloNX-XC/MeloNX/ContentView.swift +++ b/MeloNX-XC/MeloNX/ContentView.swift @@ -41,20 +41,23 @@ struct ContentView: View { ZStack { if let gameUrl, emulationStarted { VulkanSDLViewRepresentable { displayid in - gameUrl.startAccessingSecurityScopedResource() - - let config = RyujinxEmulator.Configuration( - inputPath: gameUrl.path, - mainThread: mainThread, - graphicsBackend: "Vulkan", - additionalArgs: [ - "--display-id", String(displayid), - "--fullscreen", "true" - ] - ) - - - showVirtualController(url: gameUrl, ryuconfig: config) + DispatchQueue.main.async { + + gameUrl.startAccessingSecurityScopedResource() + + let config = RyujinxEmulator.Configuration( + inputPath: gameUrl.path, + mainThread: mainThread, + graphicsBackend: "Vulkan", + additionalArgs: [ + "--display-id", String(displayid), + "--fullscreen", "true" + ] + ) + + + showVirtualController(url: gameUrl, ryuconfig: config) + } } } @@ -110,32 +113,38 @@ func startEmulation(game: URL, config: RyujinxEmulator.Configuration) { let config = config - // patchMakeKeyAndVisible() + patchMakeKeyAndVisible() // SDL_Init(SDL_INIT_VIDEO) - - let emulator = RyujinxEmulator() - do { - try emulator.startWithRunLoop(config: config) - } catch { - print(error) + DispatchQueue.main.async { + let emulator = RyujinxEmulator() + do { + try emulator.startWithRunLoop(config: config) + } catch { + print(error) + } } } func patchMakeKeyAndVisible() { - let uiwindowClass = UIWindow.self - let m1 = class_getInstanceMethod(uiwindowClass, #selector(UIWindow.makeKeyAndVisible))! - let m2 = class_getInstanceMethod(uiwindowClass, #selector(UIWindow.wdb_makeKeyAndVisible))! - method_exchangeImplementations(m1, m2) + DispatchQueue.main.async { + let uiwindowClass = UIWindow.self + let m1 = class_getInstanceMethod(uiwindowClass, #selector(UIWindow.makeKeyAndVisible))! + let m2 = class_getInstanceMethod(uiwindowClass, #selector(UIWindow.wdb_makeKeyAndVisible))! + method_exchangeImplementations(m1, m2) + } } extension UIWindow { @objc func wdb_makeKeyAndVisible() { - print("Making window key and visible...") - if #available(iOS 13.0, *) { - self.windowScene = (UIApplication.shared.connectedScenes.first! as! UIWindowScene) + DispatchQueue.main.async { + + print("Making window key and visible...") + if #available(iOS 13.0, *) { + self.windowScene = (UIApplication.shared.connectedScenes.first! as! UIWindowScene) + } + self.wdb_makeKeyAndVisible() + theWindow = self } - self.wdb_makeKeyAndVisible() - theWindow = self } } @@ -143,15 +152,20 @@ extension UIWindow { var g_gcVirtualController: GCVirtualController! @available(iOS 15.0, *) func showVirtualController(url: URL, ryuconfig: RyujinxEmulator.Configuration) { - print("Showing virtual controller...") - let config = GCVirtualController.Configuration() - config.elements = [ - GCInputDirectionalDpad, GCInputButtonA, GCInputButtonB, GCInputButtonX, GCInputButtonY, - ] - g_gcVirtualController = GCVirtualController(configuration: config) - g_gcVirtualController.connect { err in - print("Controller connect: \(String(describing: err))") - startEmulation(game: url, config: ryuconfig) + DispatchQueue.main.async { + + print("Showing virtual controller...") + let config = GCVirtualController.Configuration() + config.elements = [ + GCInputDirectionalDpad, GCInputButtonA, GCInputButtonB, GCInputButtonX, GCInputButtonY, + ] + g_gcVirtualController = GCVirtualController(configuration: config) + g_gcVirtualController.connect { err in + print("Controller connect: \(String(describing: err))") + DispatchQueue.main.async { + startEmulation(game: url, config: ryuconfig) + } + } } } diff --git a/MeloNX-XC/MeloNX/Core/Ryujinx.swift b/MeloNX-XC/MeloNX/Core/Ryujinx.swift index 0380bf2bd..b9d72dd81 100644 --- a/MeloNX-XC/MeloNX/Core/Ryujinx.swift +++ b/MeloNX-XC/MeloNX/Core/Ryujinx.swift @@ -98,6 +98,16 @@ class RyujinxEmulator { isRunning = true + DispatchQueue.main.async { + do { + try Self.start(with: config) + } catch { + Self.log("Emulation failed to start: \(error)") + self.isRunning = false + return + } + } + emulationThread = Thread { let runLoop = RunLoop.current @@ -105,6 +115,7 @@ class RyujinxEmulator { runLoop.add(port, forMode: .default) print(config.mainThread ? "Running on the main thread" : "Running on the background thread") + /* if config.mainThread { DispatchQueue.main.async { do { @@ -124,6 +135,8 @@ class RyujinxEmulator { return } } + */ + @@ -138,7 +151,7 @@ class RyujinxEmulator { emulationThread?.name = "RyujinxEmulationThread" emulationThread?.qualityOfService = .userInteractive emulationThread?.threadPriority = 0.9 - emulationThread?.start() + // emulationThread?.start() } func quickStart(romPath: String) throws { diff --git a/MeloNX-XC/MeloNX/MetalVIew.swift b/MeloNX-XC/MeloNX/MetalVIew.swift index f44351295..0ec294a0b 100644 --- a/MeloNX-XC/MeloNX/MetalVIew.swift +++ b/MeloNX-XC/MeloNX/MetalVIew.swift @@ -18,8 +18,11 @@ struct VulkanSDLViewRepresentable: UIViewRepresentable { let configure: (Uint32) -> Void func makeUIView(context: Context) -> VulkanSDLView { let view = VulkanSDLView(frame: .zero) - configure(SDL_GetWindowID(view.sdlWindow)) + DispatchQueue.main.async { [self] in + configure(SDL_GetWindowID(view.sdlWindow)) + } return view + } func updateUIView(_ uiView: VulkanSDLView, context: Context) { @@ -33,19 +36,24 @@ class VulkanSDLView: UIView { override init(frame: CGRect) { super.init(frame: frame) - initializeSDL() + DispatchQueue.main.async { [self] in + initializeSDL() + } } required init?(coder: NSCoder) { super.init(coder: coder) - initializeSDL() + DispatchQueue.main.async { [self] in + initializeSDL() + } + } private func initializeSDL() { // Initialize SDL with video support - + // Create an SDL window with Metal support DispatchQueue.main.async { [self] in sdlWindow = SDL_CreateWindow( @@ -57,25 +65,31 @@ class VulkanSDLView: UIView { SDL_WINDOW_SHOWN.rawValue | SDL_WINDOW_ALLOW_HIGHDPI.rawValue | SDL_WINDOW_VULKAN.rawValue ) } - + + guard sdlWindow != nil else { print("Error creating SDL window: \(String(cString: SDL_GetError()))") return } - + // Create SDL Metal view and attach to this UIView - metalView = SDL_Metal_CreateView(sdlWindow) - if metalView == nil { - print("Failed to create SDL Metal view.") - return + DispatchQueue.main.async { [self] in + metalView = SDL_Metal_CreateView(sdlWindow) + if metalView == nil { + print("Failed to create SDL Metal view.") + return + } } - + if let metalLayerPointer = SDL_Metal_GetLayer(metalView) { let metalLayer = Unmanaged.fromOpaque(metalLayerPointer).takeUnretainedValue() metalLayer.device = MTLCreateSystemDefaultDevice() metalLayer.pixelFormat = .bgra8Unorm - layer.addSublayer(metalLayer) + DispatchQueue.main.async { [self] in + layer.addSublayer(metalLayer) + } } + } deinit {