Improved Performance, Added support for exiting Mii Maker, Updated Touch code and more

This commit is contained in:
Stossy11 2025-06-15 19:14:34 +10:00
parent 0fe067ccf6
commit bfa3e25d9e
18 changed files with 184 additions and 71 deletions

View file

@ -788,6 +788,9 @@
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
); );
GCC_OPTIMIZATION_LEVEL = z; GCC_OPTIMIZATION_LEVEL = z;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
@ -1039,6 +1042,10 @@
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
"$(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 = "$(VERSION)"; MARKETING_VERSION = "$(VERSION)";
PRODUCT_BUNDLE_IDENTIFIER = com.stossy11.MeloNX; PRODUCT_BUNDLE_IDENTIFIER = com.stossy11.MeloNX;
@ -1197,6 +1204,9 @@
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
); );
GCC_OPTIMIZATION_LEVEL = z; GCC_OPTIMIZATION_LEVEL = z;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
@ -1448,6 +1458,10 @@
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
"$(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 = "$(VERSION)"; MARKETING_VERSION = "$(VERSION)";
PRODUCT_BUNDLE_IDENTIFIER = com.stossy11.MeloNX; PRODUCT_BUNDLE_IDENTIFIER = com.stossy11.MeloNX;

View file

@ -58,6 +58,7 @@
buildConfiguration = "Release" buildConfiguration = "Release"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
disableMainThreadChecker = "YES"
launchStyle = "0" launchStyle = "0"
useCustomWorkingDirectory = "NO" useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO" ignoresPersistentStateOnLaunch = "NO"
@ -68,7 +69,8 @@
allowLocationSimulation = "YES" allowLocationSimulation = "YES"
queueDebuggingEnabled = "No" queueDebuggingEnabled = "No"
consoleMode = "0" consoleMode = "0"
structuredConsoleMode = "2"> structuredConsoleMode = "2"
disablePerformanceAntipatternChecker = "YES">
<BuildableProductRunnable <BuildableProductRunnable
runnableDebuggingMode = "0"> runnableDebuggingMode = "0">
<BuildableReference <BuildableReference

View file

@ -16,10 +16,12 @@
#include <SDL2/SDL_syswm.h> #include <SDL2/SDL_syswm.h>
#include <StosJIT/StosJIT-Swift.h> #include <StosJIT/StosJIT-Swift.h>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
struct GameInfo { struct GameInfo {
long FileSize; long FileSize;
char TitleName[512]; char TitleName[512];
@ -41,6 +43,10 @@ struct DlcNcaList {
struct DlcNcaListItem* items; struct DlcNcaListItem* items;
}; };
typedef void (^SwiftCallback)(NSString *result);
void RegisterCallback(NSString *identifier, SwiftCallback callback);
extern struct GameInfo get_game_info(int, char*); extern struct GameInfo get_game_info(int, char*);
extern struct DlcNcaList get_dlc_nca_list(const char* titleIdPtr, const char* pathPtr); extern struct DlcNcaList get_dlc_nca_list(const char* titleIdPtr, const char* pathPtr);

View file

@ -535,9 +535,9 @@ class Ryujinx : ObservableObject {
args.append(contentsOf: ["--memory-manager-mode", config.memoryManagerMode]) args.append(contentsOf: ["--memory-manager-mode", config.memoryManagerMode])
// args.append(contentsOf: ["--exclusive-fullscreen", String(true)]) args.append(contentsOf: ["--exclusive-fullscreen", String(true)])
// args.append(contentsOf: ["--exclusive-fullscreen-width", "\(Int(UIScreen.main.bounds.width))"]) args.append(contentsOf: ["--exclusive-fullscreen-width", "\(Int(UIScreen.main.bounds.width))"])
// args.append(contentsOf: ["--exclusive-fullscreen-height", "\(Int(UIScreen.main.bounds.height))"]) args.append(contentsOf: ["--exclusive-fullscreen-height", "\(Int(UIScreen.main.bounds.height))"])
// We don't need this. Ryujinx should handle it fine :3 // We don't need this. Ryujinx should handle it fine :3
// this also causes crashes in some games :3 // this also causes crashes in some games :3

View file

@ -142,6 +142,15 @@ struct EmulationView: View {
// print(cool) // print(cool)
} }
} }
RegisterCallback("exit-emulation") { cool in
DispatchQueue.main.async {
print(cool)
startgame = nil
stop_emulation()
try? Ryujinx.shared.stop()
}
}
} }
.onChange(of: scenePhase) { newPhase in .onChange(of: scenePhase) { newPhase in
// Detect when the app enters the background // Detect when the app enters the background

View file

@ -9,10 +9,10 @@ import MetalKit
import UIKit import UIKit
class MeloMTKView: MTKView { class MeloMTKView: MTKView {
private var activeTouches: [UITouch] = [] private var activeTouches: [UITouch] = []
private var ignoredTouches: Set<UITouch> = [] private var ignoredTouches: Set<UITouch> = []
private var touchIndexMap: [UITouch: Int] = [:]
private let baseWidth: CGFloat = 1280 private let baseWidth: CGFloat = 1280
private let baseHeight: CGFloat = 720 private let baseHeight: CGFloat = 720
private var aspectRatio: AspectRatio = .fixed16x9 private var aspectRatio: AspectRatio = .fixed16x9
@ -84,83 +84,112 @@ class MeloMTKView: MTKView {
return CGPoint(x: scaledX, y: scaledY) return CGPoint(x: scaledX, y: scaledY)
} }
private func getNextAvailableIndex() -> Int {
for i in 0..<Int.max {
if !touchIndexMap.values.contains(i) {
return i
}
}
return 0
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event) super.touchesBegan(touches, with: event)
let disabled = UserDefaults.standard.bool(forKey: "disableTouch") let disabled = UserDefaults.standard.bool(forKey: "disableTouch")
guard !disabled else { return }
setAspectRatio(Ryujinx.shared.config?.aspectRatio ?? .fixed16x9) setAspectRatio(Ryujinx.shared.config?.aspectRatio ?? .fixed16x9)
if !disabled { for touch in touches {
for touch in touches { let location = touch.location(in: self)
let location = touch.location(in: self) guard let scaledLocation = scaleToTargetResolution(location) else {
if scaleToTargetResolution(location) == nil { ignoredTouches.insert(touch)
ignoredTouches.insert(touch) continue
continue
}
activeTouches.append(touch)
let index = activeTouches.firstIndex(of: touch)!
let scaledLocation = scaleToTargetResolution(location)!
// // print("Touch began at: \(scaledLocation) and \(self.aspectRatio)")
touch_began(Float(scaledLocation.x), Float(scaledLocation.y), Int32(index))
} }
let index = getNextAvailableIndex()
touchIndexMap[touch] = index
activeTouches.append(touch)
touch_began(Float(scaledLocation.x), Float(scaledLocation.y), Int32(index))
} }
} }
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) { override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesEnded(touches, with: event) super.touchesEnded(touches, with: event)
let disabled = UserDefaults.standard.bool(forKey: "disableTouch") let disabled = UserDefaults.standard.bool(forKey: "disableTouch")
guard !disabled else {
setAspectRatio(Ryujinx.shared.config?.aspectRatio ?? .fixed16x9)
if !disabled {
for touch in touches { for touch in touches {
if ignoredTouches.contains(touch) { ignoredTouches.remove(touch)
ignoredTouches.remove(touch)
continue
}
if let index = activeTouches.firstIndex(of: touch) { if let index = activeTouches.firstIndex(of: touch) {
activeTouches.remove(at: index) activeTouches.remove(at: index)
// // print("Touch ended for index \(index)")
touch_ended(Int32(index))
} }
touchIndexMap.removeValue(forKey: touch)
}
return
}
for touch in touches {
if ignoredTouches.remove(touch) != nil {
continue
}
if let touchIndex = touchIndexMap[touch] {
touch_ended(Int32(touchIndex))
if let arrayIndex = activeTouches.firstIndex(of: touch) {
activeTouches.remove(at: arrayIndex)
}
touchIndexMap.removeValue(forKey: touch)
} }
} }
} }
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) { override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesMoved(touches, with: event) super.touchesMoved(touches, with: event)
let disabled = UserDefaults.standard.bool(forKey: "disableTouch") let disabled = UserDefaults.standard.bool(forKey: "disableTouch")
guard !disabled else { return }
setAspectRatio(Ryujinx.shared.config?.aspectRatio ?? .fixed16x9) setAspectRatio(Ryujinx.shared.config?.aspectRatio ?? .fixed16x9)
if !disabled { for touch in touches {
for touch in touches { if ignoredTouches.contains(touch) {
if ignoredTouches.contains(touch) { continue
continue
}
let location = touch.location(in: self)
guard let scaledLocation = scaleToTargetResolution(location) else {
if let index = activeTouches.firstIndex(of: touch) {
activeTouches.remove(at: index)
// // print("Touch left active area, removed index \(index)")
touch_ended(Int32(index))
}
continue
}
if let index = activeTouches.firstIndex(of: touch) {
// // print("Touch moved to: \(scaledLocation)")
touch_moved(Float(scaledLocation.x), Float(scaledLocation.y), Int32(index))
}
} }
guard let touchIndex = touchIndexMap[touch] else {
continue
}
let location = touch.location(in: self)
guard let scaledLocation = scaleToTargetResolution(location) else {
touch_ended(Int32(touchIndex))
if let arrayIndex = activeTouches.firstIndex(of: touch) {
activeTouches.remove(at: arrayIndex)
}
touchIndexMap.removeValue(forKey: touch)
ignoredTouches.insert(touch)
continue
}
touch_moved(Float(scaledLocation.x), Float(scaledLocation.y), Int32(touchIndex))
} }
} }
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesCancelled(touches, with: event)
touchesEnded(touches, with: event)
}
func resetTouchTracking() {
activeTouches.removeAll()
ignoredTouches.removeAll()
touchIndexMap.removeAll()
}
} }

View file

@ -1252,3 +1252,16 @@ extension View {
} }
} }
} }
extension View {
@available(iOS, introduced: 14.0, deprecated: 19.0, message: "")
func glassEffect(_ style: Glass, in shape: some Shape) -> some View {
return self
}
}
@available(iOS, introduced: 14.0, deprecated: 19.0, message: "")
struct Glass: Hashable {
static var regular = Glass()
}

View file

@ -10,7 +10,7 @@
</data> </data>
<key>Info.plist</key> <key>Info.plist</key>
<data> <data>
RTwvCLsTMs+YfZ9ZeF25QYe7/LE= GYWZONTCP5su4yOAk0d5jCd2K88=
</data> </data>
<key>Modules/module.modulemap</key> <key>Modules/module.modulemap</key>
<data> <data>

View file

@ -10,7 +10,7 @@ namespace Ryujinx.Graphics.Vulkan
public const int MaxShaderStages = 5; public const int MaxShaderStages = 5;
public const int MaxUniformBuffersPerStage = 18; public const int MaxUniformBuffersPerStage = 18;
public const int MaxStorageBuffersPerStage = 16; public const int MaxStorageBuffersPerStage = 16;
public const int MaxTexturesPerStage = 31; public const int MaxTexturesPerStage = 18;
public const int MaxImagesPerStage = 16; public const int MaxImagesPerStage = 16;
public const int MaxUniformBufferBindings = MaxUniformBuffersPerStage * MaxShaderStages; public const int MaxUniformBufferBindings = MaxUniformBuffersPerStage * MaxShaderStages;
public const int MaxStorageBufferBindings = MaxStorageBuffersPerStage * MaxShaderStages; public const int MaxStorageBufferBindings = MaxStorageBuffersPerStage * MaxShaderStages;

View file

@ -702,12 +702,13 @@ namespace Ryujinx.Graphics.Vulkan
if (true) if (true)
{ {
try try
{
UpdateAndBindTexturesWithoutTemplate(cbs, program, pbp);
} catch (Exception e)
{ {
UpdateAndBind(cbs, program, PipelineBase.TextureSetIndex, pbp); UpdateAndBind(cbs, program, PipelineBase.TextureSetIndex, pbp);
} }
catch (Exception e)
{
UpdateAndBindTexturesWithoutTemplate(cbs, program, pbp);
}
} }
else else
{ {
@ -763,7 +764,7 @@ namespace Ryujinx.Graphics.Vulkan
if (info.Buffer.Handle == 0) if (info.Buffer.Handle == 0)
{ {
info.Buffer = dummyBuffer?.Get(cbs).Value ?? default; info.Buffer = dummyBuffer?.Get(cbs).Value ?? default;
info.Offset = 0; // info.Offset = 0;
info.Range = Vk.WholeSize; info.Range = Vk.WholeSize;
} }
@ -798,6 +799,13 @@ namespace Ryujinx.Graphics.Vulkan
} }
} }
var dummyImageInfo = new DescriptorImageInfo
{
ImageView = _dummyTexture.GetImageView().Get(cbs).Value,
Sampler = _dummySampler.GetSampler().Get(cbs).Value,
ImageLayout = ImageLayout.General
};
DescriptorSetTemplate template = program.Templates[setIndex]; DescriptorSetTemplate template = program.Templates[setIndex];
DescriptorSetTemplateWriter tu = _templateUpdater.Begin(template); DescriptorSetTemplateWriter tu = _templateUpdater.Begin(template);
@ -871,12 +879,12 @@ namespace Ryujinx.Graphics.Vulkan
if (texture.ImageView.Handle == 0) if (texture.ImageView.Handle == 0)
{ {
texture.ImageView = _dummyTexture.GetImageView().Get(cbs).Value; texture.ImageView = dummyImageInfo.ImageView;
} }
if (texture.Sampler.Handle == 0) if (texture.Sampler.Handle == 0)
{ {
texture.Sampler = _dummySampler.GetSampler().Get(cbs).Value; texture.Sampler = dummyImageInfo.Sampler;
} }
} }
@ -916,7 +924,7 @@ namespace Ryujinx.Graphics.Vulkan
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++)
{ {
images[i].ImageView = _imageRefs[binding + i].ImageView?.Get(cbs).Value ?? default; images[i].ImageView = _imageRefs[binding + i].ImageView?.Get(cbs).Value ?? dummyImageInfo.ImageView;
} }
tu.Push<DescriptorImageInfo>(images[..count]); tu.Push<DescriptorImageInfo>(images[..count]);

View file

@ -188,15 +188,21 @@ namespace Ryujinx.Graphics.Vulkan
var dsc = program.GetNewDescriptorSetCollection(setIndex, out var isNew).Get(cbs); var dsc = program.GetNewDescriptorSetCollection(setIndex, out var isNew).Get(cbs);
DescriptorSetTemplate template = program.Templates[setIndex];
DescriptorSetTemplateWriter tu = templateUpdater.Begin(template);
if (!_isBuffer) if (!_isBuffer)
{ {
dsc.UpdateImages(0, 0, GetImageInfos(_gd, cbs, dummyTexture), DescriptorType.StorageImage); tu.Push(GetImageInfos(_gd, cbs, dummyTexture));
} }
else else
{ {
dsc.UpdateBufferImages(0, 0, GetBufferViews(cbs), DescriptorType.StorageTexelBuffer); tu.Push(GetBufferViews(cbs));
} }
templateUpdater.Commit(_gd, device, sets[0]);
sets = dsc.GetSets(); sets = dsc.GetSets();
return sets; return sets;

View file

@ -159,8 +159,8 @@ namespace Ryujinx.Graphics.Vulkan
} }
texture.ImageLayout = ImageLayout.General; texture.ImageLayout = ImageLayout.General;
texture.ImageView = refs.View?.Get(cbs).Value ?? default; texture.ImageView = refs.View?.Get(cbs).Value ?? dummyTexture.GetImageView().Get(cbs).Value;
texture.Sampler = refs.Sampler?.Get(cbs).Value ?? default; texture.Sampler = refs.Sampler?.Get(cbs).Value ?? dummySampler.GetSampler().Get(cbs).Value;
if (texture.ImageView.Handle == 0) if (texture.ImageView.Handle == 0)
{ {

View file

@ -647,6 +647,15 @@ namespace Ryujinx.Graphics.Vulkan
Format.Bc7Srgb, Format.Bc7Srgb,
Format.Bc7Unorm); Format.Bc7Unorm);
if (!OperatingSystem.IsIOSVersionAtLeast(16, 4))
{
// On iOS 16.4 and later, these formats are supported.
// On earlier versions, it is not supported.
supportsBc123CompressionFormat = false;
supportsBc45CompressionFormat = false;
supportsBc67CompressionFormat = false;
}
bool supportsEtc2CompressionFormat = FormatCapabilities.OptimalFormatsSupport(compressedFormatFeatureFlags, bool supportsEtc2CompressionFormat = FormatCapabilities.OptimalFormatsSupport(compressedFormatFeatureFlags,
Format.Etc2RgbaSrgb, Format.Etc2RgbaSrgb,
Format.Etc2RgbaUnorm, Format.Etc2RgbaUnorm,
@ -714,7 +723,7 @@ namespace Ryujinx.Graphics.Vulkan
SystemMemoryType memoryType; SystemMemoryType memoryType;
if (IsSharedMemory) if (IsSharedMemory && !IsMoltenVk)
{ {
memoryType = SystemMemoryType.UnifiedMemory; memoryType = SystemMemoryType.UnifiedMemory;
} }

View file

@ -1,10 +1,14 @@
using Ryujinx.Common; using Ryujinx.Common;
using System.Runtime.InteropServices;
using System; using System;
namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.LibraryAppletProxy namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.LibraryAppletProxy
{ {
class ILibraryAppletSelfAccessor : IpcService class ILibraryAppletSelfAccessor : IpcService
{ {
[DllImport("RyujinxHelper.framework/RyujinxHelper", CallingConvention = CallingConvention.Cdecl)]
public static extern void TriggerCallback(string cIdentifier);
private readonly AppletStandalone _appletStandalone = new(); private readonly AppletStandalone _appletStandalone = new();
public ILibraryAppletSelfAccessor(ServiceCtx context) public ILibraryAppletSelfAccessor(ServiceCtx context)
@ -45,6 +49,14 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib
return ResultCode.Success; return ResultCode.Success;
} }
[CommandCmif(1)]
public ResultCode PushOutData(ServiceCtx context)
{
TriggerCallback("exit-emulation");
return ResultCode.Success;
}
[CommandCmif(11)] [CommandCmif(11)]
// GetLibraryAppletInfo() -> nn::am::service::LibraryAppletInfo // GetLibraryAppletInfo() -> nn::am::service::LibraryAppletInfo
public ResultCode GetLibraryAppletInfo(ServiceCtx context) public ResultCode GetLibraryAppletInfo(ServiceCtx context)

View file

@ -11,6 +11,11 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE
Data = data; Data = data;
} }
public byte[] GetData()
{
return Data;
}
[CommandCmif(0)] [CommandCmif(0)]
// Open() -> object<nn::am::service::IStorageAccessor> // Open() -> object<nn::am::service::IStorageAccessor>
public ResultCode Open(ServiceCtx context) public ResultCode Open(ServiceCtx context)