diff --git a/src/MeloNX/MeloNX.xcodeproj/project.pbxproj b/src/MeloNX/MeloNX.xcodeproj/project.pbxproj index 66e09e341..0664aa8fc 100644 --- a/src/MeloNX/MeloNX.xcodeproj/project.pbxproj +++ b/src/MeloNX/MeloNX.xcodeproj/project.pbxproj @@ -26,7 +26,6 @@ /* Begin PBXBuildFile section */ 4E0DED342D05695D00FEF007 /* SwiftUIJoystick in Frameworks */ = {isa = PBXBuildFile; productRef = 4E0DED332D05695D00FEF007 /* SwiftUIJoystick */; }; 4EA5AE822D16807500AD0B9F /* SwiftSVG in Frameworks */ = {isa = PBXBuildFile; productRef = 4EA5AE812D16807500AD0B9F /* SwiftSVG */; }; - 5650564B2D2A758600C8BB1E /* dotnet.xcconfig.example in Resources */ = {isa = PBXBuildFile; fileRef = 5650564A2D2A758600C8BB1E /* dotnet.xcconfig.example */; }; CA8F9C322D3F5AB200D7E586 /* GameController.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4E80AA622CD7122800029585 /* GameController.framework */; }; /* End PBXBuildFile section */ @@ -392,7 +391,6 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 5650564B2D2A758600C8BB1E /* dotnet.xcconfig.example in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -626,6 +624,8 @@ FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/MeloNX/Dependencies/XCFrameworks", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", ); GCC_OPTIMIZATION_LEVEL = fast; GENERATE_INFOPLIST_FILE = YES; @@ -644,7 +644,13 @@ "$(inherited)", "@executable_path/Frameworks", ); - LIBRARY_SEARCH_PATHS = "$(inherited)"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + ); MARKETING_VERSION = 0.0.8; PRODUCT_BUNDLE_IDENTIFIER = com.stossy11.MeloNX; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -670,6 +676,8 @@ FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/MeloNX/Dependencies/XCFrameworks", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", ); GCC_OPTIMIZATION_LEVEL = fast; GENERATE_INFOPLIST_FILE = YES; @@ -688,7 +696,13 @@ "$(inherited)", "@executable_path/Frameworks", ); - LIBRARY_SEARCH_PATHS = "$(inherited)"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + ); MARKETING_VERSION = 0.0.8; PRODUCT_BUNDLE_IDENTIFIER = com.stossy11.MeloNX; PRODUCT_NAME = "$(TARGET_NAME)"; diff --git a/src/MeloNX/MeloNX.xcodeproj/project.xcworkspace/xcuserdata/stossy11.xcuserdatad/UserInterfaceState.xcuserstate b/src/MeloNX/MeloNX.xcodeproj/project.xcworkspace/xcuserdata/stossy11.xcuserdatad/UserInterfaceState.xcuserstate index f791e4f47..4b5a5da7c 100644 Binary files a/src/MeloNX/MeloNX.xcodeproj/project.xcworkspace/xcuserdata/stossy11.xcuserdatad/UserInterfaceState.xcuserstate and b/src/MeloNX/MeloNX.xcodeproj/project.xcworkspace/xcuserdata/stossy11.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/src/MeloNX/MeloNX/App/Core/Headers/Ryujinx-Header.h b/src/MeloNX/MeloNX/App/Core/Headers/Ryujinx-Header.h index 4443e6841..9a786c8df 100644 --- a/src/MeloNX/MeloNX/App/Core/Headers/Ryujinx-Header.h +++ b/src/MeloNX/MeloNX/App/Core/Headers/Ryujinx-Header.h @@ -19,10 +19,10 @@ extern "C" { struct GameInfo { long FileSize; char TitleName[512]; - long TitleId; + char TitleId[32]; char Developer[256]; - int Version; - unsigned char* ImageData; + char Version[16]; + unsigned char* ImageData; unsigned int ImageSize; }; diff --git a/src/MeloNX/MeloNX/App/Core/Ryujinx/Controller/WaitforVC.swift b/src/MeloNX/MeloNX/App/Core/Ryujinx/Controller/WaitforVC.swift index 85698d639..c6b4d9fe4 100644 --- a/src/MeloNX/MeloNX/App/Core/Ryujinx/Controller/WaitforVC.swift +++ b/src/MeloNX/MeloNX/App/Core/Ryujinx/Controller/WaitforVC.swift @@ -33,7 +33,6 @@ func waitForController() { let controllerView = ControllerView() let newHostingController = UIHostingController(rootView: controllerView) - // Store reference globally to prevent deallocation hostingController = newHostingController let containerView = newHostingController.view! @@ -50,7 +49,7 @@ func waitForController() { SDL_SetWindowPosition(sdlWindow, 0, 0) } - timer.invalidate() // Stop the timer after adding the view + timer.invalidate() } } } diff --git a/src/MeloNX/MeloNX/App/Core/Ryujinx/Ryujinx.swift b/src/MeloNX/MeloNX/App/Core/Ryujinx/Ryujinx.swift index 04c431c44..b42154d66 100644 --- a/src/MeloNX/MeloNX/App/Core/Ryujinx/Ryujinx.swift +++ b/src/MeloNX/MeloNX/App/Core/Ryujinx/Ryujinx.swift @@ -134,12 +134,13 @@ class Ryujinx { isRunning = true - RunLoop.current.perform { - let url = URL(string: config.gamepath)! + MainThread { + + let url = URL(string: config.gamepath) do { let args = self.buildCommandLineArgs(from: config) - let accessing = url.startAccessingSecurityScopedResource() + let accessing = url?.startAccessingSecurityScopedResource() // Convert Arguments to ones that Ryujinx can Read let cArgs = args.map { strdup($0) } @@ -151,8 +152,8 @@ class Ryujinx { if result != 0 { self.isRunning = false - if accessing { - url.stopAccessingSecurityScopedResource() + if let accessing, accessing { + url!.stopAccessingSecurityScopedResource() } throw RyujinxError.executionError(code: result) @@ -176,6 +177,19 @@ class Ryujinx { var running: Bool { return isRunning } + + + func MainThread(_ block: @escaping @Sendable () -> Void) { + if #available(iOS 17.0, *) { + RunLoop.current.perform { + block() + } + } else { + DispatchQueue.main.async { + block() + } + } + } private func buildCommandLineArgs(from config: Configuration) -> [String] { var args: [String] = [] diff --git a/src/MeloNX/MeloNX/App/Models/Game.swift b/src/MeloNX/MeloNX/App/Models/Game.swift index cc11c5aa5..de6acc5c0 100644 --- a/src/MeloNX/MeloNX/App/Models/Game.swift +++ b/src/MeloNX/MeloNX/App/Models/Game.swift @@ -22,6 +22,51 @@ public struct Game: Identifiable, Equatable { var version: String var icon: UIImage? + + static func convertGameInfoToGame(gameInfo: GameInfo, url: URL) -> Game { + var gameInfo = gameInfo + var gameTemp = Game(containerFolder: url.deletingLastPathComponent(), fileType: .item, fileURL: url, titleName: "", titleId: "", developer: "", version: "") + + gameTemp.titleName = withUnsafePointer(to: &gameInfo.TitleName) { + $0.withMemoryRebound(to: UInt8.self, capacity: MemoryLayout.size(ofValue: $0)) { + String(cString: $0) + } + } + + gameTemp.developer = withUnsafePointer(to: &gameInfo.Developer) { + $0.withMemoryRebound(to: UInt8.self, capacity: MemoryLayout.size(ofValue: $0)) { + String(cString: $0) + } + } + + gameTemp.titleId = withUnsafePointer(to: &gameInfo.TitleId) { + $0.withMemoryRebound(to: UInt8.self, capacity: MemoryLayout.size(ofValue: $0)) { + String(cString: $0) + } + } + + + gameTemp.version = withUnsafePointer(to: &gameInfo.Version) { + $0.withMemoryRebound(to: UInt8.self, capacity: MemoryLayout.size(ofValue: $0)) { + String(cString: $0) + } + } + + let imageSize = Int(gameInfo.ImageSize) + if imageSize > 0, imageSize <= 1024 * 1024 { + let imageData = Data(bytes: gameInfo.ImageData, count: imageSize) + + gameTemp.icon = UIImage(data: imageData) + } else { + print("Invalid image size.") + + } + + + return gameTemp + + } + func createImage(from gameInfo: GameInfo) -> UIImage? { // Access the struct let gameInfoValue = gameInfo diff --git a/src/MeloNX/MeloNX/App/Views/ContentView.swift b/src/MeloNX/MeloNX/App/Views/ContentView.swift index f6b266595..b333c872f 100644 --- a/src/MeloNX/MeloNX/App/Views/ContentView.swift +++ b/src/MeloNX/MeloNX/App/Views/ContentView.swift @@ -52,6 +52,9 @@ struct ContentView: View { // MoltenVKSettings(string: "MVK_CONFIG_PREFILL_METAL_COMMAND_BUFFERS", value: "2"), MoltenVKSettings(string: "MVK_USE_METAL_PRIVATE_API", value: "0"), // MoltenVKSettings(string: "MVK_CONFIG_RESUME_LOST_DEVICE", value: "1"), + MoltenVKSettings(string: "MVK_CONFIG_MAX_ACTIVE_METAL_COMMAND_BUFFERS_PER_QUEUE", value: "192"), + MoltenVKSettings(string: "MVK_CONFIG_PREFILL_METAL_COMMAND_BUFFERS", value: "2"), + //MVK_CONFIG_LOG_LEVEL MoltenVKSettings(string: "MVK_CONFIG_USE_METAL_PRIVATE_API", value: "0") ] @@ -285,12 +288,6 @@ struct ContentView: View { config.gamepath = game.fileURL.path config.inputids = Array(Set(currentControllers.map(\.id))) - if mVKPreFillBuffer { - let setting = MoltenVKSettings(string: "MVK_CONFIG_PREFILL_METAL_COMMAND_BUFFERS", value: "1") - setenv(setting.string, setting.value, 1) - print("Prefill Metal Command Buffer Enabled") - } - if config.inputids.isEmpty { config.inputids.append("0") diff --git a/src/MeloNX/MeloNX/App/Views/GamesList/GameListView.swift b/src/MeloNX/MeloNX/App/Views/GamesList/GameListView.swift index a20129440..eb4cb15e9 100644 --- a/src/MeloNX/MeloNX/App/Views/GamesList/GameListView.swift +++ b/src/MeloNX/MeloNX/App/Views/GamesList/GameListView.swift @@ -223,30 +223,8 @@ struct GameLibraryView: View { var gameInfo = get_game_info(handle.fileDescriptor, extensionPtr) - var game = Game(containerFolder: url.deletingLastPathComponent(), fileType: .item, fileURL: url, titleName: "", titleId: "", developer: "", version: "") - - game.titleName = withUnsafePointer(to: &gameInfo.TitleName) { - $0.withMemoryRebound(to: UInt8.self, capacity: MemoryLayout.size(ofValue: $0)) { - String(cString: $0) - } - } - - game.developer = withUnsafePointer(to: &gameInfo.Developer) { - $0.withMemoryRebound(to: UInt8.self, capacity: MemoryLayout.size(ofValue: $0)) { - String(cString: $0) - } - } - - game.titleId = String(gameInfo.TitleId) - - print(String(gameInfo.TitleId)) - - - game.version = String(gameInfo.Version) - - game.icon = game.createImage(from: gameInfo) + let game = Game.convertGameInfoToGame(gameInfo: gameInfo, url: url) - DispatchQueue.main.async { startemu = game } @@ -321,29 +299,10 @@ struct GameLibraryView: View { let fileExtension = (fileURLCandidate.pathExtension as NSString).utf8String let extensionPtr = UnsafeMutablePointer(mutating: fileExtension) - var gameInfo = get_game_info(handle.fileDescriptor, extensionPtr) - var game = Game(containerFolder: romsDirectory, fileType: .item, fileURL: fileURLCandidate, titleName: "", titleId: "", developer: "", version: "") + let gameInfo = get_game_info(handle.fileDescriptor, extensionPtr) - game.titleName = withUnsafePointer(to: &gameInfo.TitleName) { - $0.withMemoryRebound(to: UInt8.self, capacity: MemoryLayout.size(ofValue: $0)) { - String(cString: $0) - } - } - - game.developer = withUnsafePointer(to: &gameInfo.Developer) { - $0.withMemoryRebound(to: UInt8.self, capacity: MemoryLayout.size(ofValue: $0)) { - String(cString: $0) - } - } - - game.titleId = String(gameInfo.TitleId) - - - game.version = String(gameInfo.Version) - - game.icon = game.createImage(from: gameInfo) - + let game = Game.convertGameInfoToGame(gameInfo: gameInfo, url: fileURLCandidate) games.append(game) } catch { diff --git a/src/MeloNX/MeloNX/App/Views/SettingsView/SettingsView.swift b/src/MeloNX/MeloNX/App/Views/SettingsView/SettingsView.swift index 153263bec..c53af457c 100644 --- a/src/MeloNX/MeloNX/App/Views/SettingsView/SettingsView.swift +++ b/src/MeloNX/MeloNX/App/Views/SettingsView/SettingsView.swift @@ -374,9 +374,9 @@ struct SettingsView: View { Section { DisclosureGroup { - Toggle(isOn: $mVKPreFillBuffer) { - labelWithIcon("MVK: Pre-Fill Metal Command Buffers", iconName: "gearshape") - }.tint(.blue) + // Toggle(isOn: $mVKPreFillBuffer) { + // labelWithIcon("MVK: Pre-Fill Metal Command Buffers", iconName: "gearshape") + // }.tint(.blue) HStack { labelWithIcon("Page Size", iconName: "textformat.size") diff --git a/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/libMoltenVK.dylib b/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/libMoltenVK.dylib index 7e3092cd5..ddf960dc7 100755 Binary files a/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/libMoltenVK.dylib and b/src/MeloNX/MeloNX/Dependencies/Dynamic Libraries/libMoltenVK.dylib differ diff --git a/src/MeloNX/MeloNX/Dependencies/XCFrameworks/MoltenVK.xcframework/ios-arm64/MoltenVK.framework/Info.plist b/src/MeloNX/MeloNX/Dependencies/XCFrameworks/MoltenVK.xcframework/ios-arm64/MoltenVK.framework/Info.plist index 195d07044..2e0914e03 100644 Binary files a/src/MeloNX/MeloNX/Dependencies/XCFrameworks/MoltenVK.xcframework/ios-arm64/MoltenVK.framework/Info.plist and b/src/MeloNX/MeloNX/Dependencies/XCFrameworks/MoltenVK.xcframework/ios-arm64/MoltenVK.framework/Info.plist differ diff --git a/src/MeloNX/MeloNX/Dependencies/XCFrameworks/MoltenVK.xcframework/ios-arm64/MoltenVK.framework/MoltenVK b/src/MeloNX/MeloNX/Dependencies/XCFrameworks/MoltenVK.xcframework/ios-arm64/MoltenVK.framework/MoltenVK index d26ed962f..ddf960dc7 100755 Binary files a/src/MeloNX/MeloNX/Dependencies/XCFrameworks/MoltenVK.xcframework/ios-arm64/MoltenVK.framework/MoltenVK and b/src/MeloNX/MeloNX/Dependencies/XCFrameworks/MoltenVK.xcframework/ios-arm64/MoltenVK.framework/MoltenVK differ diff --git a/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs b/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs index 6bb5a9875..a12e880a7 100644 --- a/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs +++ b/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs @@ -580,6 +580,7 @@ namespace Ryujinx.Graphics.Vulkan { texture.Sampler = _dummySampler.GetSampler().Get(cbs).Value; } + if (OperatingSystem.IsIOS()) { Span singleTexture = textures.Slice(i, 1); dsc.UpdateImages(0, binding + i, singleTexture, DescriptorType.CombinedImageSampler); diff --git a/src/Ryujinx.Graphics.Vulkan/MoltenVK/MVKInitialization.cs b/src/Ryujinx.Graphics.Vulkan/MoltenVK/MVKInitialization.cs index 75114590f..aab25899f 100644 --- a/src/Ryujinx.Graphics.Vulkan/MoltenVK/MVKInitialization.cs +++ b/src/Ryujinx.Graphics.Vulkan/MoltenVK/MVKInitialization.cs @@ -23,7 +23,10 @@ namespace Ryujinx.Graphics.Vulkan.MoltenVK config.UseMetalArgumentBuffers = true; - config.SemaphoreSupportStyle = MVKVkSemaphoreSupportStyle.MVK_CONFIG_VK_SEMAPHORE_SUPPORT_STYLE_SINGLE_QUEUE; + if (OperatingSystem.IsIOSVersionAtLeast(17)) { + config.SemaphoreSupportStyle = MVKVkSemaphoreSupportStyle.MVK_CONFIG_VK_SEMAPHORE_SUPPORT_STYLE_SINGLE_QUEUE; + } + config.SynchronousQueueSubmits = false; config.ResumeLostDevice = true; diff --git a/src/Ryujinx.Headless.SDL2/Program.cs b/src/Ryujinx.Headless.SDL2/Program.cs index 2f9fce03e..fca399c48 100644 --- a/src/Ryujinx.Headless.SDL2/Program.cs +++ b/src/Ryujinx.Headless.SDL2/Program.cs @@ -322,7 +322,14 @@ namespace Ryujinx.Headless.SDL2 var gameInfo = GetGameInfo(stream, extension); - return new GameInfoNative(gameInfo.FileSize, gameInfo.TitleName, gameInfo.TitleId, gameInfo.Developer, gameInfo.Version, gameInfo.Icon); + return new GameInfoNative( + (ulong)gameInfo.FileSize, + gameInfo.TitleName, + gameInfo.TitleId, + gameInfo.Developer, + gameInfo.Version, + gameInfo.Icon + ); } public static GameInfo? GetGameInfo(Stream gameStream, string extension) @@ -1482,42 +1489,41 @@ namespace Ryujinx.Headless.SDL2 { public ulong FileSize; public fixed byte TitleName[512]; - public ulong TitleId; + public fixed byte TitleId[32]; public fixed byte Developer[256]; - public uint Version; + public fixed byte Version[16]; public byte* ImageData; public uint ImageSize; - public GameInfoNative(ulong fileSize, string titleName, ulong titleId, string developer, uint version, byte[] imageData) + public GameInfoNative(ulong fileSize, string titleName, string titleId, string developer, string version, byte[] imageData) { FileSize = fileSize; - TitleId = titleId; - Version = version; - fixed (byte* developerPtr = Developer) fixed (byte* titleNamePtr = TitleName) + fixed (byte* titleIdPtr = TitleId) + fixed (byte* developerPtr = Developer) + fixed (byte* versionPtr = Version) { CopyStringToFixedArray(titleName, titleNamePtr, 512); + CopyStringToFixedArray(titleId, titleIdPtr, 32); CopyStringToFixedArray(developer, developerPtr, 256); + CopyStringToFixedArray(version, versionPtr, 16); } if (imageData == null || imageData.Length > 4096 * 4096) { - // throw new ArgumentException("Image data must not exceed 4 MB."); - ImageSize = (uint)0; + ImageSize = 0; ImageData = null; } else { ImageSize = (uint)imageData.Length; - ImageData = (byte*)Marshal.AllocHGlobal(imageData.Length); - Marshal.Copy(imageData, 0, (IntPtr)ImageData, imageData.Length); } } - // Don't forget to free the allocated memory + // Free allocated memory for ImageData public void Dispose() { if (ImageData != null) @@ -1526,17 +1532,14 @@ namespace Ryujinx.Headless.SDL2 ImageData = null; } } + private static void CopyStringToFixedArray(string source, byte* destination, int length) { var span = new Span(destination, length); + span.Clear(); Encoding.UTF8.GetBytes(source, span); } - - private static void CopyArrayToFixedArray(byte[] source, byte* destination, int maxLength) - { - var span = new Span(destination, maxLength); - source.AsSpan().CopyTo(span); - } } + } }