mirror of
https://git.743378673.xyz/MeloNX/MeloNX.git
synced 2025-06-28 03:16:23 +02:00
Compare commits
7 commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
71056542c4 | ||
![]() |
2348f5f4b1 | ||
![]() |
bfa3e25d9e | ||
![]() |
0fe067ccf6 | ||
![]() |
2e77bb0345 | ||
![]() |
83bf8aecae | ||
![]() |
e2e26eed8b |
25 changed files with 328 additions and 201 deletions
17
README.md
17
README.md
|
@ -28,6 +28,10 @@ MeloNX works on iPhone XS/XR and later and iPad 8th Gen and later. Check out the
|
|||
- Recommended Device: iPhone 15 Pro or newer.
|
||||
- Low-End Recommended Device: iPhone 13 Pro.
|
||||
|
||||
## Discord Server
|
||||
|
||||
We have a discord server!
|
||||
- https://discord.gg/melonx
|
||||
|
||||
## How to install
|
||||
|
||||
|
@ -141,12 +145,12 @@ If having Issues installing firmware (Make sure your keys are installed first)
|
|||
|
||||
- **GPU**
|
||||
|
||||
The GPU emulator emulates the Switch's Maxwell GPU using Metal (via MoltenVK) APIs through a custom build of OpenTK or Silk.NET respectively.
|
||||
The GPU emulator emulates the Switch's Maxwell GPU using Metal (via MoltenVK) APIs through a custom build of Silk.NET.
|
||||
|
||||
- **Input**
|
||||
|
||||
We currently have support for keyboard, touch input, JoyCon input support, and nearly all controllers.
|
||||
Motion controls are natively supported in most cases.
|
||||
We currently have support for keyboard, touch input, JoyCon input support, and nearly all MFI controllers.
|
||||
Motion controls are natively supported in most cases, however JoyCons do not have motion support doe to an iOS limitation.
|
||||
|
||||
- **DLC & Modifications**
|
||||
|
||||
|
@ -157,14 +161,13 @@ If having Issues installing firmware (Make sure your keys are installed first)
|
|||
|
||||
The emulator has settings for enabling or disabling some logging, remapping controllers, and more.
|
||||
|
||||
## License
|
||||
# License
|
||||
|
||||
This software is licensed under the terms of the [MeloNX license (Based on MIT License)](LICENSE.txt).
|
||||
This software is licensed under the terms of the [MeloNX license](LICENSE.txt).
|
||||
This project makes use of code authored by the libvpx project, licensed under BSD and the ffmpeg project, licensed under LGPLv3.
|
||||
See [LICENSE.txt](LICENSE.txt) and [THIRDPARTY.md](distribution/legal/THIRDPARTY.md) for more details.
|
||||
|
||||
## Credits
|
||||
|
||||
# Credits
|
||||
- [Ryujinx](https://github.com/ryujinx-mirror/ryujinx) is used for the base of this emulator. (link is to ryujinx-mirror since they were supportive)
|
||||
- [LibHac](https://github.com/Thealexbarney/LibHac) is used for our file-system.
|
||||
- [AmiiboAPI](https://www.amiiboapi.com) is used in our Amiibo emulation.
|
||||
|
|
|
@ -8,4 +8,4 @@
|
|||
// Configuration settings file format documentation can be found at:
|
||||
// https://help.apple.com/xcode/#/dev745c5c974
|
||||
|
||||
VERSION = 2.0
|
||||
VERSION = 2.0.1
|
||||
|
|
|
@ -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",
|
||||
);
|
||||
GCC_OPTIMIZATION_LEVEL = z;
|
||||
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",
|
||||
);
|
||||
MARKETING_VERSION = "$(VERSION)";
|
||||
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",
|
||||
);
|
||||
GCC_OPTIMIZATION_LEVEL = z;
|
||||
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",
|
||||
);
|
||||
MARKETING_VERSION = "$(VERSION)";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.stossy11.MeloNX;
|
||||
|
|
Binary file not shown.
|
@ -58,6 +58,7 @@
|
|||
buildConfiguration = "Release"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
disableMainThreadChecker = "YES"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
|
@ -68,7 +69,8 @@
|
|||
allowLocationSimulation = "YES"
|
||||
queueDebuggingEnabled = "No"
|
||||
consoleMode = "0"
|
||||
structuredConsoleMode = "2">
|
||||
structuredConsoleMode = "2"
|
||||
disablePerformanceAntipatternChecker = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
|
|
|
@ -16,10 +16,12 @@
|
|||
#include <SDL2/SDL_syswm.h>
|
||||
#include <StosJIT/StosJIT-Swift.h>
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
struct GameInfo {
|
||||
long FileSize;
|
||||
char TitleName[512];
|
||||
|
@ -41,6 +43,10 @@ struct DlcNcaList {
|
|||
struct DlcNcaListItem* items;
|
||||
};
|
||||
|
||||
typedef void (^SwiftCallback)(NSString *result);
|
||||
|
||||
void RegisterCallback(NSString *identifier, SwiftCallback callback);
|
||||
|
||||
extern struct GameInfo get_game_info(int, char*);
|
||||
|
||||
extern struct DlcNcaList get_dlc_nca_list(const char* titleIdPtr, const char* pathPtr);
|
||||
|
|
|
@ -62,7 +62,6 @@ func allocateTest() -> Bool {
|
|||
|
||||
memcpy(jitMemory, code, code.count)
|
||||
|
||||
|
||||
if mprotect(jitMemory, pageSize, PROT_READ | PROT_EXEC) != 0 {
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -12,8 +12,7 @@ class NativeController: Hashable, BaseController {
|
|||
private var instanceID: SDL_JoystickID = -1
|
||||
private var controller: OpaquePointer?
|
||||
private var nativeController: GCController
|
||||
private var controllerMotionProvider: ControllerMotionProvider?
|
||||
private var deviceMotionProvider: DeviceMotionProvider?
|
||||
private var controllerMotionProvider: DSUMotionProvider?
|
||||
|
||||
private let controllerHaptics: CHHapticEngine?
|
||||
private let rumbleController: RumbleController?
|
||||
|
@ -22,21 +21,21 @@ class NativeController: Hashable, BaseController {
|
|||
|
||||
init(_ controller: GCController) {
|
||||
nativeController = controller
|
||||
controllerHaptics = nativeController.haptics?.createEngine(withLocality: .default)
|
||||
var ncontrollerHaptics = nativeController.haptics?.createEngine(withLocality: .default)
|
||||
|
||||
let vendorName = nativeController.vendorName ?? "Unknown"
|
||||
var usesdeviceHaptics = (ncontrollerHaptics == nil || vendorName.lowercased().hasSuffix("backbone") || vendorName.lowercased() == "backbone one")
|
||||
controllerHaptics = usesdeviceHaptics ? ncontrollerHaptics : try? CHHapticEngine()
|
||||
|
||||
// Make sure the haptic engine exists before attempting to start it or initialize the controller.
|
||||
if let hapticsEngine = controllerHaptics {
|
||||
do {
|
||||
try hapticsEngine.start()
|
||||
rumbleController = RumbleController(engine: hapticsEngine, rumbleMultiplier: 2.5)
|
||||
|
||||
// print("CHHapticEngine started and RumbleController initialized.")
|
||||
rumbleController = RumbleController(engine: hapticsEngine, rumbleMultiplier: 1.2)
|
||||
} catch {
|
||||
// print("Error starting CHHapticEngine: \(error.localizedDescription)")
|
||||
rumbleController = nil
|
||||
}
|
||||
} else {
|
||||
// print("CHHapticEngine is nil. Cannot initialize RumbleController.")
|
||||
rumbleController = nil
|
||||
}
|
||||
setupHandheldController()
|
||||
|
@ -49,27 +48,17 @@ class NativeController: Hashable, BaseController {
|
|||
internal func tryRegisterMotion(slot: UInt8) {
|
||||
// Setup Motion
|
||||
let dsuServer = DSUServer.shared
|
||||
let vendorName = nativeController.vendorName ?? "Unknown"
|
||||
var usesdevicemotion = (vendorName.lowercased() == "Joy-Con (l/R)".lowercased() || vendorName.lowercased().hasSuffix("backbone") || vendorName.lowercased() == "backbone one")
|
||||
|
||||
if nativeController.vendorName?.lowercased() == "Joy-Con (l/R)".lowercased() {
|
||||
deviceMotionProvider = DeviceMotionProvider(slot: slot)
|
||||
if let provider = deviceMotionProvider {
|
||||
dsuServer.register(provider)
|
||||
}
|
||||
} else {
|
||||
controllerMotionProvider = ControllerMotionProvider(controller: nativeController, slot: slot)
|
||||
if let provider = controllerMotionProvider {
|
||||
dsuServer.register(provider)
|
||||
}
|
||||
controllerMotionProvider = usesdevicemotion ? DeviceMotionProvider(slot: slot) : ControllerMotionProvider(controller: nativeController, slot: slot)
|
||||
|
||||
if let provider = controllerMotionProvider {
|
||||
dsuServer.register(provider)
|
||||
}
|
||||
}
|
||||
|
||||
internal func tryGetMotionProvider() -> DSUMotionProvider? {
|
||||
if nativeController.vendorName == "Joy-Con (l/R)" {
|
||||
return deviceMotionProvider
|
||||
} else {
|
||||
return controllerMotionProvider
|
||||
}
|
||||
}
|
||||
internal func tryGetMotionProvider() -> DSUMotionProvider? { return controllerMotionProvider }
|
||||
|
||||
private func setupHandheldController() {
|
||||
if SDL_WasInit(Uint32(SDL_INIT_GAMECONTROLLER)) == 0 {
|
||||
|
@ -94,38 +83,39 @@ class NativeController: Hashable, BaseController {
|
|||
},
|
||||
SetPlayerIndex: { userdata, playerIndex in
|
||||
// print("Player index set to \(playerIndex)")
|
||||
guard let userdata, let player = GCControllerPlayerIndex(rawValue: Int(playerIndex)) else { return }
|
||||
let _self = Unmanaged<NativeController>.fromOpaque(userdata).takeUnretainedValue()
|
||||
_self.nativeController.playerIndex = player
|
||||
},
|
||||
Rumble: { userdata, lowFreq, highFreq in
|
||||
// print("Rumble with \(lowFreq), \(highFreq)")
|
||||
guard let userdata else { return 0 }
|
||||
let _self = Unmanaged<NativeController>.fromOpaque(userdata).takeUnretainedValue()
|
||||
_self.rumbleController?.rumble(lowFreq: Float(lowFreq), highFreq: Float(highFreq))
|
||||
return 0
|
||||
},
|
||||
RumbleTriggers: { userdata, leftRumble, rightRumble in
|
||||
// print("Trigger rumble with \(leftRumble), \(rightRumble)")
|
||||
return 0
|
||||
},
|
||||
SetLED: { userdata, red, green, blue in
|
||||
// print("Set LED to RGB(\(red), \(green), \(blue))")
|
||||
guard let userdata else { return 0 }
|
||||
let _self = Unmanaged<NativeController>.fromOpaque(userdata).takeUnretainedValue()
|
||||
guard let light = _self.nativeController.light else { return 0 }
|
||||
light.color = .init(red: Float(red), green: Float(green), blue: Float(blue))
|
||||
return 0
|
||||
},
|
||||
SendEffect: { userdata, data, size in
|
||||
// print("Effect sent with size \(size)")
|
||||
return 0
|
||||
}
|
||||
)
|
||||
|
||||
instanceID = SDL_JoystickAttachVirtualEx(&joystickDesc)// SDL_JoystickAttachVirtual(SDL_JoystickType(SDL_JOYSTICK_TYPE_GAMECONTROLLER.rawValue), 6, 15, 1)
|
||||
instanceID = SDL_JoystickAttachVirtualEx(&joystickDesc)
|
||||
if instanceID < 0 {
|
||||
// print("Failed to create virtual joystick: \(String(cString: SDL_GetError()))")
|
||||
return
|
||||
}
|
||||
|
||||
controller = SDL_GameControllerOpen(Int32(instanceID))
|
||||
|
||||
if controller == nil {
|
||||
// print("Failed to create virtual controller: \(String(cString: SDL_GetError()))")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -214,7 +204,6 @@ class NativeController: Hashable, BaseController {
|
|||
func setButtonState(_ state: Uint8, for button: VirtualControllerButton) {
|
||||
guard controller != nil else { return }
|
||||
|
||||
// // print("Button: \(button.rawValue) {state: \(state)}")
|
||||
if (button == .leftTrigger || button == .rightTrigger) && (state == 1 || state == 0) {
|
||||
let axis: SDL_GameControllerAxis = (button == .leftTrigger) ? SDL_CONTROLLER_AXIS_TRIGGERLEFT : SDL_CONTROLLER_AXIS_TRIGGERRIGHT
|
||||
let value: Int = (state == 1) ? 32767 : 0
|
||||
|
|
|
@ -213,7 +213,6 @@ class Ryujinx : ObservableObject {
|
|||
var language: SystemLanguage = .americanEnglish
|
||||
var regioncode: SystemRegionCode = .usa
|
||||
var handHeldController: Bool = true
|
||||
var backendMultithreading: Bool = true
|
||||
|
||||
|
||||
init(gamepath: String = "",
|
||||
|
@ -242,7 +241,6 @@ class Ryujinx : ObservableObject {
|
|||
language: SystemLanguage = .americanEnglish,
|
||||
regioncode: SystemRegionCode = .usa,
|
||||
handHeldController: Bool = false,
|
||||
backendMultithreading: Bool = true
|
||||
) {
|
||||
self.gamepath = gamepath
|
||||
self.inputids = inputids
|
||||
|
@ -270,7 +268,6 @@ class Ryujinx : ObservableObject {
|
|||
self.language = language
|
||||
self.regioncode = regioncode
|
||||
self.handHeldController = handHeldController
|
||||
self.backendMultithreading = backendMultithreading
|
||||
}
|
||||
|
||||
|
||||
|
@ -538,9 +535,9 @@ class Ryujinx : ObservableObject {
|
|||
|
||||
args.append(contentsOf: ["--memory-manager-mode", config.memoryManagerMode])
|
||||
|
||||
// args.append(contentsOf: ["--exclusive-fullscreen", String(true)])
|
||||
// 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", String(true)])
|
||||
args.append(contentsOf: ["--exclusive-fullscreen-width", "\(Int(UIScreen.main.bounds.width))"])
|
||||
args.append(contentsOf: ["--exclusive-fullscreen-height", "\(Int(UIScreen.main.bounds.height))"])
|
||||
// We don't need this. Ryujinx should handle it fine :3
|
||||
// this also causes crashes in some games :3
|
||||
|
||||
|
@ -568,13 +565,9 @@ class Ryujinx : ObservableObject {
|
|||
|
||||
args.append(contentsOf: ["--aspect-ratio", config.aspectRatio.rawValue])
|
||||
|
||||
// args.append(contentsOf: ["--system-timezone", TimeZone.current.identifier])
|
||||
args.append(contentsOf: ["--system-timezone", TimeZone.current.identifier])
|
||||
|
||||
args.append(contentsOf: ["--system-time-offset", String(TimeZone.current.secondsFromGMT())])
|
||||
|
||||
if !config.backendMultithreading {
|
||||
args.append(contentsOf: ["--backend-multithreading", "Off"])
|
||||
}
|
||||
// args.append(contentsOf: ["--system-time-offset", String(TimeZone.current.secondsFromGMT())])
|
||||
|
||||
|
||||
if config.nintendoinput {
|
||||
|
|
|
@ -264,7 +264,6 @@ struct ABXYView: View {
|
|||
|
||||
struct ButtonView: View {
|
||||
var button: VirtualControllerButton
|
||||
var callback: (() -> Void)? = nil
|
||||
|
||||
@AppStorage("onscreenhandheld") var onscreenjoy: Bool = false
|
||||
@AppStorage("On-ScreenControllerScale") var controllerScale: Double = 1.0
|
||||
|
@ -345,30 +344,24 @@ struct ButtonView: View {
|
|||
}
|
||||
|
||||
private func handleButtonPress() {
|
||||
if let callback {
|
||||
callback()
|
||||
} else {
|
||||
guard !isPressed || istoggle else { return }
|
||||
guard !isPressed || istoggle else { return }
|
||||
|
||||
if istoggle {
|
||||
toggleState.toggle()
|
||||
isPressed = toggleState
|
||||
let value = toggleState ? 1 : 0
|
||||
Ryujinx.shared.virtualController.setButtonState(Uint8(value), for: button)
|
||||
Haptics.shared.play(.medium)
|
||||
} else {
|
||||
isPressed = true
|
||||
Ryujinx.shared.virtualController.setButtonState(1, for: button)
|
||||
Haptics.shared.play(.medium)
|
||||
}
|
||||
if istoggle {
|
||||
toggleState.toggle()
|
||||
isPressed = toggleState
|
||||
let value = toggleState ? 1 : 0
|
||||
Ryujinx.shared.virtualController.setButtonState(Uint8(value), for: button)
|
||||
Haptics.shared.play(.medium)
|
||||
} else {
|
||||
isPressed = true
|
||||
Ryujinx.shared.virtualController.setButtonState(1, for: button)
|
||||
Haptics.shared.play(.medium)
|
||||
}
|
||||
}
|
||||
|
||||
private func handleButtonRelease() {
|
||||
if istoggle { return }
|
||||
|
||||
if let callback { return }
|
||||
|
||||
guard isPressed else { return }
|
||||
|
||||
isPressed = false
|
||||
|
@ -404,23 +397,40 @@ struct ButtonView: View {
|
|||
// Centralized button configuration
|
||||
private var buttonConfig: ButtonConfiguration {
|
||||
switch button {
|
||||
case .A: return .init(iconName: "a.circle.fill")
|
||||
case .B: return .init(iconName: "b.circle.fill")
|
||||
case .X: return .init(iconName: "x.circle.fill")
|
||||
case .Y: return .init(iconName: "y.circle.fill")
|
||||
case .leftStick: return .init(iconName: "l.joystick.press.down.fill")
|
||||
case .rightStick: return .init(iconName: "r.joystick.press.down.fill")
|
||||
case .dPadUp: return .init(iconName: "arrowtriangle.up.circle.fill")
|
||||
case .dPadDown: return .init(iconName: "arrowtriangle.down.circle.fill")
|
||||
case .dPadLeft: return .init(iconName: "arrowtriangle.left.circle.fill")
|
||||
case .dPadRight: return .init(iconName: "arrowtriangle.right.circle.fill")
|
||||
case .leftTrigger: return .init(iconName: "zl.rectangle.roundedtop.fill")
|
||||
case .rightTrigger: return .init(iconName: "zr.rectangle.roundedtop.fill")
|
||||
case .leftShoulder: return .init(iconName: "l.rectangle.roundedbottom.fill")
|
||||
case .rightShoulder: return .init(iconName: "r.rectangle.roundedbottom.fill")
|
||||
case .start: return .init(iconName: "plus.circle.fill")
|
||||
case .back: return .init(iconName: "minus.circle.fill")
|
||||
case .guide: return .init(iconName: "gearshape.fill")
|
||||
case .A:
|
||||
return ButtonConfiguration(iconName: "a.circle.fill")
|
||||
case .B:
|
||||
return ButtonConfiguration(iconName: "b.circle.fill")
|
||||
case .X:
|
||||
return ButtonConfiguration(iconName: "x.circle.fill")
|
||||
case .Y:
|
||||
return ButtonConfiguration(iconName: "y.circle.fill")
|
||||
case .leftStick:
|
||||
return ButtonConfiguration(iconName: "l.joystick.press.down.fill")
|
||||
case .rightStick:
|
||||
return ButtonConfiguration(iconName: "r.joystick.press.down.fill")
|
||||
case .dPadUp:
|
||||
return ButtonConfiguration(iconName: "arrowtriangle.up.circle.fill")
|
||||
case .dPadDown:
|
||||
return ButtonConfiguration(iconName: "arrowtriangle.down.circle.fill")
|
||||
case .dPadLeft:
|
||||
return ButtonConfiguration(iconName: "arrowtriangle.left.circle.fill")
|
||||
case .dPadRight:
|
||||
return ButtonConfiguration(iconName: "arrowtriangle.right.circle.fill")
|
||||
case .leftTrigger:
|
||||
return ButtonConfiguration(iconName: "zl.rectangle.roundedtop.fill")
|
||||
case .rightTrigger:
|
||||
return ButtonConfiguration(iconName: "zr.rectangle.roundedtop.fill")
|
||||
case .leftShoulder:
|
||||
return ButtonConfiguration(iconName: "l.rectangle.roundedbottom.fill")
|
||||
case .rightShoulder:
|
||||
return ButtonConfiguration(iconName: "r.rectangle.roundedbottom.fill")
|
||||
case .start:
|
||||
return ButtonConfiguration(iconName: "plus.circle.fill")
|
||||
case .back:
|
||||
return ButtonConfiguration(iconName: "minus.circle.fill")
|
||||
case .guide:
|
||||
return ButtonConfiguration(iconName: "house.circle.fill")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -142,6 +142,15 @@ struct EmulationView: View {
|
|||
// 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
|
||||
// Detect when the app enters the background
|
||||
|
|
|
@ -35,7 +35,6 @@ class InGameSettingsManager: PerGameSettingsManaging {
|
|||
Ryujinx.shared.config = config[currentgame.titleId]
|
||||
let args = Ryujinx.shared.buildCommandLineArgs(from: config[currentgame.titleId] ?? Ryujinx.Arguments())
|
||||
|
||||
// Convert Arguments to ones that Ryujinx can Read
|
||||
let cArgs = args.map { strdup($0) }
|
||||
defer { cArgs.forEach { free($0) } }
|
||||
var argvPtrs = cArgs
|
||||
|
|
|
@ -9,9 +9,9 @@ import MetalKit
|
|||
import UIKit
|
||||
|
||||
class MeloMTKView: MTKView {
|
||||
|
||||
private var activeTouches: [UITouch] = []
|
||||
private var ignoredTouches: Set<UITouch> = []
|
||||
private var touchIndexMap: [UITouch: Int] = [:]
|
||||
|
||||
private let baseWidth: CGFloat = 1280
|
||||
private let baseHeight: CGFloat = 720
|
||||
|
@ -84,28 +84,35 @@ class MeloMTKView: MTKView {
|
|||
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?) {
|
||||
super.touchesBegan(touches, with: event)
|
||||
|
||||
let disabled = UserDefaults.standard.bool(forKey: "disableTouch")
|
||||
guard !disabled else { return }
|
||||
|
||||
setAspectRatio(Ryujinx.shared.config?.aspectRatio ?? .fixed16x9)
|
||||
|
||||
if !disabled {
|
||||
for touch in touches {
|
||||
let location = touch.location(in: self)
|
||||
if scaleToTargetResolution(location) == nil {
|
||||
ignoredTouches.insert(touch)
|
||||
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))
|
||||
for touch in touches {
|
||||
let location = touch.location(in: self)
|
||||
guard let scaledLocation = scaleToTargetResolution(location) else {
|
||||
ignoredTouches.insert(touch)
|
||||
continue
|
||||
}
|
||||
|
||||
let index = getNextAvailableIndex()
|
||||
touchIndexMap[touch] = index
|
||||
activeTouches.append(touch)
|
||||
|
||||
touch_began(Float(scaledLocation.x), Float(scaledLocation.y), Int32(index))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -113,22 +120,29 @@ class MeloMTKView: MTKView {
|
|||
super.touchesEnded(touches, with: event)
|
||||
|
||||
let disabled = UserDefaults.standard.bool(forKey: "disableTouch")
|
||||
|
||||
setAspectRatio(Ryujinx.shared.config?.aspectRatio ?? .fixed16x9)
|
||||
|
||||
if !disabled {
|
||||
guard !disabled else {
|
||||
for touch in touches {
|
||||
if ignoredTouches.contains(touch) {
|
||||
ignoredTouches.remove(touch)
|
||||
continue
|
||||
}
|
||||
|
||||
ignoredTouches.remove(touch)
|
||||
if let index = activeTouches.firstIndex(of: touch) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -137,30 +151,45 @@ class MeloMTKView: MTKView {
|
|||
super.touchesMoved(touches, with: event)
|
||||
|
||||
let disabled = UserDefaults.standard.bool(forKey: "disableTouch")
|
||||
guard !disabled else { return }
|
||||
|
||||
setAspectRatio(Ryujinx.shared.config?.aspectRatio ?? .fixed16x9)
|
||||
|
||||
if !disabled {
|
||||
for touch in touches {
|
||||
if ignoredTouches.contains(touch) {
|
||||
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))
|
||||
}
|
||||
for touch in touches {
|
||||
if ignoredTouches.contains(touch) {
|
||||
continue
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -81,8 +81,6 @@ struct ContentView: View {
|
|||
|
||||
_settings = State(initialValue: defaultSettings)
|
||||
|
||||
// print(SDL_CONTROLLER_BUTTON_LEFTSTICK.rawValue)
|
||||
|
||||
initializeSDL()
|
||||
}
|
||||
|
||||
|
@ -132,7 +130,6 @@ struct ContentView: View {
|
|||
JITPopover() {
|
||||
ryujinx.jitenabled = false
|
||||
}
|
||||
// .interactiveDismissDisabled()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -153,11 +150,8 @@ struct ContentView: View {
|
|||
refreshControllersList()
|
||||
}
|
||||
|
||||
|
||||
UserDefaults.standard.set(false, forKey: "lockInApp")
|
||||
|
||||
// print(MTLHud.shared.isEnabled)
|
||||
|
||||
initControllerObservers()
|
||||
|
||||
Air.play(AnyView(
|
||||
|
@ -166,7 +160,6 @@ struct ContentView: View {
|
|||
|
||||
refreshControllersList()
|
||||
|
||||
|
||||
ryujinx.addGames()
|
||||
|
||||
checkJitStatus()
|
||||
|
@ -287,7 +280,6 @@ struct ContentView: View {
|
|||
queue: .main
|
||||
) { notification in
|
||||
if let controller = notification.object as? GCController {
|
||||
// print("Controller connected: \(controller.productCategory)")
|
||||
nativeControllers[controller] = .init(controller)
|
||||
refreshControllersList()
|
||||
}
|
||||
|
@ -299,7 +291,8 @@ struct ContentView: View {
|
|||
queue: .main
|
||||
) { notification in
|
||||
if let controller = notification.object as? GCController {
|
||||
// print("Controller disconnected: \(controller.productCategory)")
|
||||
currentControllers = []
|
||||
controllersList = []
|
||||
nativeControllers[controller]?.cleanup()
|
||||
nativeControllers[controller] = nil
|
||||
refreshControllersList()
|
||||
|
@ -316,6 +309,9 @@ struct ContentView: View {
|
|||
}
|
||||
|
||||
private func refreshControllersList() {
|
||||
currentControllers = []
|
||||
controllersList = []
|
||||
|
||||
controllersList = ryujinx.getConnectedControllers()
|
||||
|
||||
if let onscreen = controllersList.first(where: { $0.name == ryujinx.virtualController.controllername }) {
|
||||
|
@ -325,8 +321,6 @@ struct ContentView: View {
|
|||
controllersList.removeAll(where: { $0.id == "0" || (!$0.name.starts(with: "GC - ") && $0 != onscreencontroller) })
|
||||
controllersList.mutableForEach { $0.name = $0.name.replacingOccurrences(of: "GC - ", with: "") }
|
||||
|
||||
currentControllers = []
|
||||
|
||||
if controllersList.count == 1 {
|
||||
currentControllers.append(controllersList[0])
|
||||
} else if (controllersList.count - 1) >= 1 {
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -1129,9 +1129,6 @@ struct SettingsViewNew: View {
|
|||
|
||||
Divider()
|
||||
|
||||
SettingsToggle(isOn: config.backendMultithreading, icon: "inset.filled.rectangle.and.person.filled", label: "Backend Multithreading")
|
||||
|
||||
Divider()
|
||||
|
||||
if MTLHud.shared.canMetalHud {
|
||||
SettingsToggle(isOn: $metalHudEnabler.metalHudEnabled, icon: "speedometer", label: "Metal Performance HUD")
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -10,7 +10,7 @@
|
|||
</data>
|
||||
<key>Info.plist</key>
|
||||
<data>
|
||||
RTwvCLsTMs+YfZ9ZeF25QYe7/LE=
|
||||
GYWZONTCP5su4yOAk0d5jCd2K88=
|
||||
</data>
|
||||
<key>Modules/module.modulemap</key>
|
||||
<data>
|
||||
|
|
|
@ -699,13 +699,21 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
|
||||
if (_dirty.HasFlag(DirtyFlags.Texture))
|
||||
{
|
||||
if (false)
|
||||
if (true)
|
||||
{
|
||||
UpdateAndBindTexturesWithoutTemplate(cbs, program, pbp);
|
||||
try
|
||||
{
|
||||
UpdateAndBind(cbs, program, PipelineBase.TextureSetIndex, pbp);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
UpdateAndBindTexturesWithoutTemplate(cbs, program, pbp);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
try {
|
||||
try
|
||||
{
|
||||
UpdateAndBind(cbs, program, PipelineBase.TextureSetIndex, pbp);
|
||||
}
|
||||
catch (Exception e)
|
||||
|
@ -756,7 +764,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
if (info.Buffer.Handle == 0)
|
||||
{
|
||||
info.Buffer = dummyBuffer?.Get(cbs).Value ?? default;
|
||||
info.Offset = 0;
|
||||
// info.Offset = 0;
|
||||
info.Range = Vk.WholeSize;
|
||||
}
|
||||
|
||||
|
@ -791,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];
|
||||
|
||||
DescriptorSetTemplateWriter tu = _templateUpdater.Begin(template);
|
||||
|
@ -864,12 +879,12 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
|
||||
if (texture.ImageView.Handle == 0)
|
||||
{
|
||||
texture.ImageView = _dummyTexture.GetImageView().Get(cbs).Value;
|
||||
texture.ImageView = dummyImageInfo.ImageView;
|
||||
}
|
||||
|
||||
if (texture.Sampler.Handle == 0)
|
||||
{
|
||||
texture.Sampler = _dummySampler.GetSampler().Get(cbs).Value;
|
||||
texture.Sampler = dummyImageInfo.Sampler;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -909,7 +924,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
|
||||
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]);
|
||||
|
@ -946,23 +961,25 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
_gd.Api.CmdBindDescriptorSets(cbs.CommandBuffer, pbp, _program.PipelineLayout, (uint)setIndex, 1, sets, 0, ReadOnlySpan<uint>.Empty);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void UpdateAndBindTexturesWithoutTemplate(CommandBufferScoped cbs, ShaderCollection program, PipelineBindPoint pbp)
|
||||
{
|
||||
int setIndex = PipelineBase.TextureSetIndex;
|
||||
ResourceBindingSegment[] bindingSegments = program.BindingSegments[setIndex];
|
||||
var bindingSegments = program.BindingSegments[setIndex];
|
||||
|
||||
if (bindingSegments.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_updateDescriptorCacheCbIndex)
|
||||
var dummyImageInfo = new DescriptorImageInfo
|
||||
{
|
||||
_updateDescriptorCacheCbIndex = false;
|
||||
program.UpdateDescriptorCacheCommandBufferIndex(cbs.CommandBufferIndex);
|
||||
}
|
||||
ImageView = _dummyTexture.GetImageView().Get(cbs).Value,
|
||||
Sampler = _dummySampler.GetSampler().Get(cbs).Value,
|
||||
ImageLayout = ImageLayout.General
|
||||
};
|
||||
|
||||
DescriptorSetCollection dsc = program.GetNewDescriptorSetCollection(setIndex, out _).Get(cbs);
|
||||
var dsc = program.GetNewDescriptorSetCollection(setIndex, out _).Get(cbs);
|
||||
|
||||
foreach (ResourceBindingSegment segment in bindingSegments)
|
||||
{
|
||||
|
@ -973,56 +990,78 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
{
|
||||
if (segment.Type != ResourceType.BufferTexture)
|
||||
{
|
||||
Span<DescriptorImageInfo> textures = _textures;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
ref DescriptorImageInfo texture = ref textures[i];
|
||||
ref TextureRef refs = ref _textureRefs[binding + i];
|
||||
int index = binding + i;
|
||||
ref var textureRef = ref _textureRefs[index];
|
||||
|
||||
texture.ImageView = refs.ImageView?.Get(cbs).Value ?? default;
|
||||
texture.Sampler = refs.Sampler?.Get(cbs).Value ?? default;
|
||||
var imageView = textureRef.ImageView?.Get(cbs).Value ?? dummyImageInfo.ImageView;
|
||||
var sampler = textureRef.Sampler?.Get(cbs).Value ?? dummyImageInfo.Sampler;
|
||||
|
||||
if (texture.ImageView.Handle == 0)
|
||||
var imageInfo = new DescriptorImageInfo
|
||||
{
|
||||
texture.ImageView = _dummyTexture.GetImageView().Get(cbs).Value;
|
||||
}
|
||||
ImageView = imageView.Handle != 0 ? imageView : dummyImageInfo.ImageView,
|
||||
Sampler = sampler.Handle != 0 ? sampler : dummyImageInfo.Sampler,
|
||||
ImageLayout = ImageLayout.General
|
||||
};
|
||||
|
||||
if (texture.Sampler.Handle == 0)
|
||||
{
|
||||
texture.Sampler = _dummySampler.GetSampler().Get(cbs).Value;
|
||||
}
|
||||
dsc.UpdateImages(0, index, new[] { imageInfo }, DescriptorType.CombinedImageSampler);
|
||||
}
|
||||
|
||||
dsc.UpdateImages(0, binding, textures[..count], DescriptorType.CombinedImageSampler);
|
||||
}
|
||||
else
|
||||
{
|
||||
Span<BufferView> bufferTextures = _bufferTextures;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
bufferTextures[i] = _bufferTextureRefs[binding + i]?.GetBufferView(cbs, false) ?? default;
|
||||
int index = binding + i;
|
||||
var bufferView = _bufferTextureRefs[index]?.GetBufferView(cbs, false) ?? default;
|
||||
dsc.UpdateBufferImages(0, index, new[] { bufferView }, DescriptorType.UniformTexelBuffer);
|
||||
}
|
||||
|
||||
dsc.UpdateBufferImages(0, binding, bufferTextures[..count], DescriptorType.UniformTexelBuffer);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var arrayRef = _textureArrayRefs[binding];
|
||||
|
||||
if (segment.Type != ResourceType.BufferTexture)
|
||||
{
|
||||
dsc.UpdateImages(0, binding, _textureArrayRefs[binding].Array.GetImageInfos(_gd, cbs, _dummyTexture, _dummySampler), DescriptorType.CombinedImageSampler);
|
||||
var imageInfos = arrayRef.Array.GetImageInfos(_gd, cbs, _dummyTexture, _dummySampler);
|
||||
if (imageInfos != null)
|
||||
{
|
||||
for (int i = 0; i < imageInfos.Length && i < count; i++)
|
||||
{
|
||||
dsc.UpdateImages(0, binding + i, new[] { imageInfos[i] }, DescriptorType.CombinedImageSampler);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
dsc.UpdateImages(0, binding + i, new[] { dummyImageInfo }, DescriptorType.CombinedImageSampler);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dsc.UpdateBufferImages(0, binding, _textureArrayRefs[binding].Array.GetBufferViews(cbs), DescriptorType.UniformTexelBuffer);
|
||||
var bufferViews = arrayRef.Array.GetBufferViews(cbs);
|
||||
if (bufferViews != null)
|
||||
{
|
||||
for (int i = 0; i < bufferViews.Length && i < count; i++)
|
||||
{
|
||||
dsc.UpdateBufferImages(0, binding + i, new[] { bufferViews[i] }, DescriptorType.UniformTexelBuffer);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
dsc.UpdateBufferImages(0, binding + i, new[] { default(BufferView) }, DescriptorType.UniformTexelBuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DescriptorSet[] sets = dsc.GetSets();
|
||||
|
||||
var sets = dsc.GetSets();
|
||||
_gd.Api.CmdBindDescriptorSets(cbs.CommandBuffer, pbp, _program.PipelineLayout, (uint)setIndex, 1, sets, 0, ReadOnlySpan<uint>.Empty);
|
||||
}
|
||||
|
||||
|
|
|
@ -188,15 +188,21 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
|
||||
var dsc = program.GetNewDescriptorSetCollection(setIndex, out var isNew).Get(cbs);
|
||||
|
||||
DescriptorSetTemplate template = program.Templates[setIndex];
|
||||
|
||||
DescriptorSetTemplateWriter tu = templateUpdater.Begin(template);
|
||||
|
||||
if (!_isBuffer)
|
||||
{
|
||||
dsc.UpdateImages(0, 0, GetImageInfos(_gd, cbs, dummyTexture), DescriptorType.StorageImage);
|
||||
tu.Push(GetImageInfos(_gd, cbs, dummyTexture));
|
||||
}
|
||||
else
|
||||
{
|
||||
dsc.UpdateBufferImages(0, 0, GetBufferViews(cbs), DescriptorType.StorageTexelBuffer);
|
||||
tu.Push(GetBufferViews(cbs));
|
||||
}
|
||||
|
||||
templateUpdater.Commit(_gd, device, sets[0]);
|
||||
|
||||
sets = dsc.GetSets();
|
||||
|
||||
return sets;
|
||||
|
|
|
@ -159,8 +159,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
}
|
||||
|
||||
texture.ImageLayout = ImageLayout.General;
|
||||
texture.ImageView = refs.View?.Get(cbs).Value ?? default;
|
||||
texture.Sampler = refs.Sampler?.Get(cbs).Value ?? default;
|
||||
texture.ImageView = refs.View?.Get(cbs).Value ?? dummyTexture.GetImageView().Get(cbs).Value;
|
||||
texture.Sampler = refs.Sampler?.Get(cbs).Value ?? dummySampler.GetSampler().Get(cbs).Value;
|
||||
|
||||
if (texture.ImageView.Handle == 0)
|
||||
{
|
||||
|
|
|
@ -647,6 +647,14 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
Format.Bc7Srgb,
|
||||
Format.Bc7Unorm);
|
||||
|
||||
if (!OperatingSystem.IsIOSVersionAtLeast(16, 4))
|
||||
{
|
||||
// On iOS 16.3.1 and earlier, these formats are not supported.
|
||||
supportsBc123CompressionFormat = false;
|
||||
supportsBc45CompressionFormat = false;
|
||||
supportsBc67CompressionFormat = false;
|
||||
}
|
||||
|
||||
bool supportsEtc2CompressionFormat = FormatCapabilities.OptimalFormatsSupport(compressedFormatFeatureFlags,
|
||||
Format.Etc2RgbaSrgb,
|
||||
Format.Etc2RgbaUnorm,
|
||||
|
@ -714,7 +722,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
|
||||
SystemMemoryType memoryType;
|
||||
|
||||
if (IsSharedMemory)
|
||||
if (IsSharedMemory && !IsMoltenVk)
|
||||
{
|
||||
memoryType = SystemMemoryType.UnifiedMemory;
|
||||
}
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
using Ryujinx.Common;
|
||||
using System.Runtime.InteropServices;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.LibraryAppletProxy
|
||||
{
|
||||
class ILibraryAppletSelfAccessor : IpcService
|
||||
{
|
||||
[DllImport("RyujinxHelper.framework/RyujinxHelper", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void TriggerCallback(string cIdentifier);
|
||||
|
||||
private readonly AppletStandalone _appletStandalone = new();
|
||||
|
||||
public ILibraryAppletSelfAccessor(ServiceCtx context)
|
||||
|
@ -45,6 +49,14 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib
|
|||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(1)]
|
||||
public ResultCode PushOutData(ServiceCtx context)
|
||||
{
|
||||
TriggerCallback("exit-emulation");
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
|
||||
[CommandCmif(11)]
|
||||
// GetLibraryAppletInfo() -> nn::am::service::LibraryAppletInfo
|
||||
public ResultCode GetLibraryAppletInfo(ServiceCtx context)
|
||||
|
|
|
@ -11,6 +11,11 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE
|
|||
Data = data;
|
||||
}
|
||||
|
||||
public byte[] GetData()
|
||||
{
|
||||
return Data;
|
||||
}
|
||||
|
||||
[CommandCmif(0)]
|
||||
// Open() -> object<nn::am::service::IStorageAccessor>
|
||||
public ResultCode Open(ServiceCtx context)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue