Put everything on the main thread for testing.

This commit is contained in:
stossy11 2024-11-02 12:37:08 +11:00
parent 60cfb86774
commit 02901a5a14
4 changed files with 93 additions and 52 deletions

View File

@ -41,20 +41,23 @@ struct ContentView: View {
ZStack { ZStack {
if let gameUrl, emulationStarted { if let gameUrl, emulationStarted {
VulkanSDLViewRepresentable { displayid in VulkanSDLViewRepresentable { displayid in
gameUrl.startAccessingSecurityScopedResource() DispatchQueue.main.async {
let config = RyujinxEmulator.Configuration( gameUrl.startAccessingSecurityScopedResource()
inputPath: gameUrl.path,
mainThread: mainThread, let config = RyujinxEmulator.Configuration(
graphicsBackend: "Vulkan", inputPath: gameUrl.path,
additionalArgs: [ mainThread: mainThread,
"--display-id", String(displayid), graphicsBackend: "Vulkan",
"--fullscreen", "true" additionalArgs: [
] "--display-id", String(displayid),
) "--fullscreen", "true"
]
)
showVirtualController(url: gameUrl, ryuconfig: config)
showVirtualController(url: gameUrl, ryuconfig: config)
}
} }
} }
@ -110,32 +113,38 @@ func startEmulation(game: URL, config: RyujinxEmulator.Configuration) {
let config = config let config = config
// patchMakeKeyAndVisible() patchMakeKeyAndVisible()
// SDL_Init(SDL_INIT_VIDEO) // SDL_Init(SDL_INIT_VIDEO)
DispatchQueue.main.async {
let emulator = RyujinxEmulator() let emulator = RyujinxEmulator()
do { do {
try emulator.startWithRunLoop(config: config) try emulator.startWithRunLoop(config: config)
} catch { } catch {
print(error) print(error)
}
} }
} }
func patchMakeKeyAndVisible() { func patchMakeKeyAndVisible() {
let uiwindowClass = UIWindow.self DispatchQueue.main.async {
let m1 = class_getInstanceMethod(uiwindowClass, #selector(UIWindow.makeKeyAndVisible))! let uiwindowClass = UIWindow.self
let m2 = class_getInstanceMethod(uiwindowClass, #selector(UIWindow.wdb_makeKeyAndVisible))! let m1 = class_getInstanceMethod(uiwindowClass, #selector(UIWindow.makeKeyAndVisible))!
method_exchangeImplementations(m1, m2) let m2 = class_getInstanceMethod(uiwindowClass, #selector(UIWindow.wdb_makeKeyAndVisible))!
method_exchangeImplementations(m1, m2)
}
} }
extension UIWindow { extension UIWindow {
@objc func wdb_makeKeyAndVisible() { @objc func wdb_makeKeyAndVisible() {
print("Making window key and visible...") DispatchQueue.main.async {
if #available(iOS 13.0, *) {
self.windowScene = (UIApplication.shared.connectedScenes.first! as! UIWindowScene) 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! var g_gcVirtualController: GCVirtualController!
@available(iOS 15.0, *) @available(iOS 15.0, *)
func showVirtualController(url: URL, ryuconfig: RyujinxEmulator.Configuration) { func showVirtualController(url: URL, ryuconfig: RyujinxEmulator.Configuration) {
print("Showing virtual controller...") DispatchQueue.main.async {
let config = GCVirtualController.Configuration()
config.elements = [ print("Showing virtual controller...")
GCInputDirectionalDpad, GCInputButtonA, GCInputButtonB, GCInputButtonX, GCInputButtonY, let config = GCVirtualController.Configuration()
] config.elements = [
g_gcVirtualController = GCVirtualController(configuration: config) GCInputDirectionalDpad, GCInputButtonA, GCInputButtonB, GCInputButtonX, GCInputButtonY,
g_gcVirtualController.connect { err in ]
print("Controller connect: \(String(describing: err))") g_gcVirtualController = GCVirtualController(configuration: config)
startEmulation(game: url, config: ryuconfig) g_gcVirtualController.connect { err in
print("Controller connect: \(String(describing: err))")
DispatchQueue.main.async {
startEmulation(game: url, config: ryuconfig)
}
}
} }
} }

View File

@ -98,6 +98,16 @@ class RyujinxEmulator {
isRunning = true 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 { emulationThread = Thread {
let runLoop = RunLoop.current let runLoop = RunLoop.current
@ -105,6 +115,7 @@ class RyujinxEmulator {
runLoop.add(port, forMode: .default) runLoop.add(port, forMode: .default)
print(config.mainThread ? "Running on the main thread" : "Running on the background thread") print(config.mainThread ? "Running on the main thread" : "Running on the background thread")
/*
if config.mainThread { if config.mainThread {
DispatchQueue.main.async { DispatchQueue.main.async {
do { do {
@ -124,6 +135,8 @@ class RyujinxEmulator {
return return
} }
} }
*/
@ -138,7 +151,7 @@ class RyujinxEmulator {
emulationThread?.name = "RyujinxEmulationThread" emulationThread?.name = "RyujinxEmulationThread"
emulationThread?.qualityOfService = .userInteractive emulationThread?.qualityOfService = .userInteractive
emulationThread?.threadPriority = 0.9 emulationThread?.threadPriority = 0.9
emulationThread?.start() // emulationThread?.start()
} }
func quickStart(romPath: String) throws { func quickStart(romPath: String) throws {

View File

@ -18,8 +18,11 @@ struct VulkanSDLViewRepresentable: UIViewRepresentable {
let configure: (Uint32) -> Void let configure: (Uint32) -> Void
func makeUIView(context: Context) -> VulkanSDLView { func makeUIView(context: Context) -> VulkanSDLView {
let view = VulkanSDLView(frame: .zero) let view = VulkanSDLView(frame: .zero)
configure(SDL_GetWindowID(view.sdlWindow)) DispatchQueue.main.async { [self] in
configure(SDL_GetWindowID(view.sdlWindow))
}
return view return view
} }
func updateUIView(_ uiView: VulkanSDLView, context: Context) { func updateUIView(_ uiView: VulkanSDLView, context: Context) {
@ -33,19 +36,24 @@ class VulkanSDLView: UIView {
override init(frame: CGRect) { override init(frame: CGRect) {
super.init(frame: frame) super.init(frame: frame)
initializeSDL() DispatchQueue.main.async { [self] in
initializeSDL()
}
} }
required init?(coder: NSCoder) { required init?(coder: NSCoder) {
super.init(coder: coder) super.init(coder: coder)
initializeSDL() DispatchQueue.main.async { [self] in
initializeSDL()
}
} }
private func initializeSDL() { private func initializeSDL() {
// Initialize SDL with video support // Initialize SDL with video support
// Create an SDL window with Metal support // Create an SDL window with Metal support
DispatchQueue.main.async { [self] in DispatchQueue.main.async { [self] in
sdlWindow = SDL_CreateWindow( sdlWindow = SDL_CreateWindow(
@ -57,25 +65,31 @@ class VulkanSDLView: UIView {
SDL_WINDOW_SHOWN.rawValue | SDL_WINDOW_ALLOW_HIGHDPI.rawValue | SDL_WINDOW_VULKAN.rawValue SDL_WINDOW_SHOWN.rawValue | SDL_WINDOW_ALLOW_HIGHDPI.rawValue | SDL_WINDOW_VULKAN.rawValue
) )
} }
guard sdlWindow != nil else { guard sdlWindow != nil else {
print("Error creating SDL window: \(String(cString: SDL_GetError()))") print("Error creating SDL window: \(String(cString: SDL_GetError()))")
return return
} }
// Create SDL Metal view and attach to this UIView // Create SDL Metal view and attach to this UIView
metalView = SDL_Metal_CreateView(sdlWindow) DispatchQueue.main.async { [self] in
if metalView == nil { metalView = SDL_Metal_CreateView(sdlWindow)
print("Failed to create SDL Metal view.") if metalView == nil {
return print("Failed to create SDL Metal view.")
return
}
} }
if let metalLayerPointer = SDL_Metal_GetLayer(metalView) { if let metalLayerPointer = SDL_Metal_GetLayer(metalView) {
let metalLayer = Unmanaged<CAMetalLayer>.fromOpaque(metalLayerPointer).takeUnretainedValue() let metalLayer = Unmanaged<CAMetalLayer>.fromOpaque(metalLayerPointer).takeUnretainedValue()
metalLayer.device = MTLCreateSystemDefaultDevice() metalLayer.device = MTLCreateSystemDefaultDevice()
metalLayer.pixelFormat = .bgra8Unorm metalLayer.pixelFormat = .bgra8Unorm
layer.addSublayer(metalLayer) DispatchQueue.main.async { [self] in
layer.addSublayer(metalLayer)
}
} }
} }
deinit { deinit {