forked from MeloNX/MeloNX
Add MetalHUD, Resolution Controls, change default settings
This commit is contained in:
parent
45e2785e93
commit
a14aadf878
@ -538,6 +538,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 = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.stossy11.MeloNX;
|
PRODUCT_BUNDLE_IDENTIFIER = com.stossy11.MeloNX;
|
||||||
@ -614,6 +618,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 = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.stossy11.MeloNX;
|
PRODUCT_BUNDLE_IDENTIFIER = com.stossy11.MeloNX;
|
||||||
|
Binary file not shown.
@ -35,21 +35,5 @@
|
|||||||
</Actions>
|
</Actions>
|
||||||
</BreakpointContent>
|
</BreakpointContent>
|
||||||
</BreakpointProxy>
|
</BreakpointProxy>
|
||||||
<BreakpointProxy
|
|
||||||
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
|
||||||
<BreakpointContent
|
|
||||||
uuid = "8F440CBF-CC8B-401B-8C2C-9DBA7AE75C40"
|
|
||||||
shouldBeEnabled = "No"
|
|
||||||
ignoreCount = "0"
|
|
||||||
continueAfterRunningActions = "No"
|
|
||||||
filePath = "MeloNX/Views/SDLView/SDLView.swift"
|
|
||||||
startingColumnNumber = "9223372036854775807"
|
|
||||||
endingColumnNumber = "9223372036854775807"
|
|
||||||
startingLineNumber = "60"
|
|
||||||
endingLineNumber = "60"
|
|
||||||
landmarkName = "makeSDLWindow()"
|
|
||||||
landmarkType = "7">
|
|
||||||
</BreakpointContent>
|
|
||||||
</BreakpointProxy>
|
|
||||||
</Breakpoints>
|
</Breakpoints>
|
||||||
</Bucket>
|
</Bucket>
|
||||||
|
58
src/MeloNX/MeloNX/Core/MetalHUD/MTLHUD.swift
Normal file
58
src/MeloNX/MeloNX/Core/MetalHUD/MTLHUD.swift
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
//
|
||||||
|
// MTLHUD.swift
|
||||||
|
// MeloNX
|
||||||
|
//
|
||||||
|
// Created by Stossy11 on 26/11/2024.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
class MTLHud {
|
||||||
|
|
||||||
|
var canMetalHud: Bool {
|
||||||
|
return openMetalDylib()
|
||||||
|
}
|
||||||
|
|
||||||
|
var isEnabled: Bool {
|
||||||
|
if let getenv = getenv("MTL_HUD_ENABLED") {
|
||||||
|
return String(cString: getenv).contains("1")
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
static let shared = MTLHud()
|
||||||
|
|
||||||
|
private init() {
|
||||||
|
if UserDefaults.standard.bool(forKey: "MTL_HUD_ENABLED") {
|
||||||
|
enable()
|
||||||
|
} else {
|
||||||
|
disable()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func openMetalDylib() -> Bool {
|
||||||
|
let path = "/usr/lib/libMTLHud.dylib"
|
||||||
|
|
||||||
|
// Load the dynamic library
|
||||||
|
if dlopen(path, RTLD_NOW) != nil {
|
||||||
|
// Library loaded successfully
|
||||||
|
print("Library loaded from \(path)")
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
// Handle error
|
||||||
|
if let error = String(validatingUTF8: dlerror()) {
|
||||||
|
print("Error loading library: \(error)")
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func enable() {
|
||||||
|
setenv("MTL_HUD_ENABLED", "1", 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func disable() {
|
||||||
|
setenv("MTL_HUD_ENABLED", "0", 1)
|
||||||
|
}
|
||||||
|
}
|
@ -34,9 +34,14 @@ class Ryujinx {
|
|||||||
|
|
||||||
@Published var controllerMap: [Controller] = []
|
@Published var controllerMap: [Controller] = []
|
||||||
|
|
||||||
|
static let shared = Ryujinx()
|
||||||
|
|
||||||
|
private init() {}
|
||||||
|
|
||||||
public struct Configuration : Codable {
|
public struct Configuration : Codable {
|
||||||
var gamepath: String
|
var gamepath: String
|
||||||
var inputids: [String]
|
var inputids: [String]
|
||||||
|
var resscale: Float
|
||||||
var debuglogs: Bool
|
var debuglogs: Bool
|
||||||
var tracelogs: Bool
|
var tracelogs: Bool
|
||||||
var listinputids: Bool
|
var listinputids: Bool
|
||||||
@ -56,11 +61,13 @@ class Ryujinx {
|
|||||||
listinputids: Bool = false,
|
listinputids: Bool = false,
|
||||||
fullscreen: Bool = true,
|
fullscreen: Bool = true,
|
||||||
memoryManagerMode: String = "HostMapped",
|
memoryManagerMode: String = "HostMapped",
|
||||||
disableVSync: Bool = true,
|
disableVSync: Bool = false,
|
||||||
disableShaderCache: Bool = false,
|
disableShaderCache: Bool = false,
|
||||||
disableDockedMode: Bool = true,
|
disableDockedMode: Bool = false,
|
||||||
enableTextureRecompression: Bool = true,
|
enableTextureRecompression: Bool = true,
|
||||||
additionalArgs: [String] = []) {
|
additionalArgs: [String] = [],
|
||||||
|
resscale: Float = 1.00
|
||||||
|
) {
|
||||||
self.gamepath = gamepath
|
self.gamepath = gamepath
|
||||||
self.inputids = inputids
|
self.inputids = inputids
|
||||||
self.debuglogs = debuglogs
|
self.debuglogs = debuglogs
|
||||||
@ -73,10 +80,10 @@ class Ryujinx {
|
|||||||
self.enableTextureRecompression = enableTextureRecompression
|
self.enableTextureRecompression = enableTextureRecompression
|
||||||
self.additionalArgs = additionalArgs
|
self.additionalArgs = additionalArgs
|
||||||
self.memoryManagerMode = memoryManagerMode
|
self.memoryManagerMode = memoryManagerMode
|
||||||
|
self.resscale = resscale
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
func start(with config: Configuration) throws {
|
func start(with config: Configuration) throws {
|
||||||
guard !isRunning else {
|
guard !isRunning else {
|
||||||
@ -84,6 +91,7 @@ class Ryujinx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
isRunning = true
|
isRunning = true
|
||||||
|
|
||||||
// Start The Emulation on the main thread
|
// Start The Emulation on the main thread
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
do {
|
do {
|
||||||
@ -139,6 +147,10 @@ class Ryujinx {
|
|||||||
args.append(contentsOf: ["--exclusive-fullscreen-height", "720"])
|
args.append(contentsOf: ["--exclusive-fullscreen-height", "720"])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if config.resscale != 1 {
|
||||||
|
args.append(contentsOf: ["--resolution-scale", String(config.resscale)])
|
||||||
|
}
|
||||||
|
|
||||||
// Adding default args directly into additionalArgs
|
// Adding default args directly into additionalArgs
|
||||||
if config.disableVSync {
|
if config.disableVSync {
|
||||||
args.append("--disable-vsync")
|
args.append("--disable-vsync")
|
||||||
|
@ -73,7 +73,7 @@ struct ContentView: View {
|
|||||||
GameListView(startemu: $game)
|
GameListView(startemu: $game)
|
||||||
.onAppear() {
|
.onAppear() {
|
||||||
Timer.scheduledTimer(withTimeInterval: 1.0, repeats: false) { _ in
|
Timer.scheduledTimer(withTimeInterval: 1.0, repeats: false) { _ in
|
||||||
controllersList = Ryujinx().getConnectedControllers()
|
controllersList = Ryujinx.shared.getConnectedControllers()
|
||||||
controllersList.removeAll(where: { $0.id == "0" })
|
controllersList.removeAll(where: { $0.id == "0" })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -88,7 +88,7 @@ struct ContentView: View {
|
|||||||
}
|
}
|
||||||
Section("Controller") {
|
Section("Controller") {
|
||||||
Button {
|
Button {
|
||||||
controllersList = Ryujinx().getConnectedControllers()
|
controllersList = Ryujinx.shared.getConnectedControllers()
|
||||||
controllersList.removeAll(where: { $0.id == "0" })
|
controllersList.removeAll(where: { $0.id == "0" })
|
||||||
} label: {
|
} label: {
|
||||||
Text("Refresh")
|
Text("Refresh")
|
||||||
@ -128,10 +128,12 @@ struct ContentView: View {
|
|||||||
allocateSixGB()
|
allocateSixGB()
|
||||||
|
|
||||||
// Start the emulation
|
// Start the emulation
|
||||||
|
|
||||||
|
print("Is MetalHud Enabled? " + (MTLHud.shared.isEnabled ? "yeah" : "nope"))
|
||||||
do {
|
do {
|
||||||
setupVirtualController()
|
setupVirtualController()
|
||||||
|
|
||||||
try Ryujinx().start(with: config)
|
try Ryujinx.shared.start(with: config)
|
||||||
|
|
||||||
|
|
||||||
} catch {
|
} catch {
|
||||||
|
@ -17,47 +17,66 @@ struct SettingsView: View {
|
|||||||
("SoftwarePageTable", "Software")
|
("SoftwarePageTable", "Software")
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@AppStorage("MTL_HUD_ENABLED") var metalHUDEnabled: Bool = false
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Form {
|
ScrollView {
|
||||||
Section(header: Text("Graphics and Performance")) {
|
VStack {
|
||||||
Toggle("Ryujinx Fullscreen", isOn: $config.fullscreen)
|
Section(header: Text("Graphics and Performance")) {
|
||||||
Toggle("Disable V-Sync", isOn: $config.disableVSync)
|
Toggle("Ryujinx Fullscreen", isOn: $config.fullscreen)
|
||||||
Toggle("Disable Shader Cache", isOn: $config.disableShaderCache)
|
Toggle("Disable V-Sync", isOn: $config.disableVSync)
|
||||||
Toggle("Enable Texture Recompression", isOn: $config.enableTextureRecompression)
|
Toggle("Disable Shader Cache", isOn: $config.disableShaderCache)
|
||||||
}
|
Toggle("Enable Texture Recompression", isOn: $config.enableTextureRecompression)
|
||||||
|
Resolution(value: $config.resscale)
|
||||||
Section(header: Text("Input Settings")) {
|
Toggle("Enable Metal HUD", isOn: $metalHUDEnabled)
|
||||||
Toggle("List Input IDs", isOn: $config.listinputids)
|
.onChange(of: metalHUDEnabled) { newValue in
|
||||||
// Toggle("Host Mapped Memory", isOn: $config.hostMappedMemory)
|
if newValue {
|
||||||
Toggle("Disable Docked Mode", isOn: $config.disableDockedMode)
|
MTLHud.shared.enable()
|
||||||
}
|
} else {
|
||||||
|
MTLHud.shared.disable()
|
||||||
Section(header: Text("Logging Settings")) {
|
}
|
||||||
Toggle("Enable Debug Logs", isOn: $config.debuglogs)
|
}
|
||||||
Toggle("Enable Trace Logs", isOn: $config.tracelogs)
|
}
|
||||||
}
|
|
||||||
Section(header: Text("CPU Mode")) {
|
Section(header: Text("Input Settings")) {
|
||||||
Picker("Memory Manager Mode", selection: $config.memoryManagerMode) {
|
Toggle("List Input IDs", isOn: $config.listinputids)
|
||||||
ForEach(memoryManagerModes, id: \.0) { key, displayName in
|
// Toggle("Host Mapped Memory", isOn: $config.hostMappedMemory)
|
||||||
Text(displayName).tag(key)
|
Toggle("Disable Docked Mode", isOn: $config.disableDockedMode)
|
||||||
|
}
|
||||||
|
|
||||||
|
Section(header: Text("Logging Settings")) {
|
||||||
|
Toggle("Enable Debug Logs", isOn: $config.debuglogs)
|
||||||
|
Toggle("Enable Trace Logs", isOn: $config.tracelogs)
|
||||||
|
}
|
||||||
|
Section(header: Text("CPU Mode")) {
|
||||||
|
HStack {
|
||||||
|
Spacer()
|
||||||
|
Picker("Memory Manager Mode", selection: $config.memoryManagerMode) {
|
||||||
|
ForEach(memoryManagerModes, id: \.0) { key, displayName in
|
||||||
|
Text(displayName).tag(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.pickerStyle(MenuPickerStyle()) // Dropdown style
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.pickerStyle(MenuPickerStyle()) // Dropdown style
|
|
||||||
}
|
|
||||||
|
|
||||||
Section(header: Text("Additional Settings")) {
|
|
||||||
//TextField("Game Path", text: $config.gamepath)
|
|
||||||
|
|
||||||
TextField("Additional Arguments", text: Binding(
|
|
||||||
get: {
|
|
||||||
config.additionalArgs.joined(separator: ", ")
|
Section(header: Text("Additional Settings")) {
|
||||||
},
|
//TextField("Game Path", text: $config.gamepath)
|
||||||
set: { newValue in
|
|
||||||
config.additionalArgs = newValue.split(separator: ",").map { String($0).trimmingCharacters(in: .whitespaces) }
|
TextField("Additional Arguments", text: Binding(
|
||||||
}
|
get: {
|
||||||
))
|
config.additionalArgs.joined(separator: ", ")
|
||||||
|
},
|
||||||
|
set: { newValue in
|
||||||
|
config.additionalArgs = newValue.split(separator: ",").map { String($0).trimmingCharacters(in: .whitespaces) }
|
||||||
|
}
|
||||||
|
))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.padding()
|
||||||
.onAppear {
|
.onAppear {
|
||||||
if let configs = loadSettings() {
|
if let configs = loadSettings() {
|
||||||
self.config = configs
|
self.config = configs
|
||||||
@ -86,3 +105,56 @@ struct SettingsView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct Resolution: View {
|
||||||
|
@Binding var value: Float
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
HStack {
|
||||||
|
Text("Resolution Scale (Custom):")
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
Button(action: {
|
||||||
|
if value > 0.1 { // Prevent values going below 0.1
|
||||||
|
value -= 0.10
|
||||||
|
value = round(value * 1000) / 1000 // Round to two decimal places
|
||||||
|
}
|
||||||
|
print(value)
|
||||||
|
}) {
|
||||||
|
Text("-")
|
||||||
|
.frame(width: 30, height: 30)
|
||||||
|
.background(Color.gray.opacity(0.2))
|
||||||
|
.cornerRadius(5)
|
||||||
|
}
|
||||||
|
|
||||||
|
TextField("", value: $value, formatter: NumberFormatter.floatFormatter)
|
||||||
|
.multilineTextAlignment(.center)
|
||||||
|
.frame(width: 60)
|
||||||
|
.textFieldStyle(RoundedBorderTextFieldStyle())
|
||||||
|
.keyboardType(.decimalPad)
|
||||||
|
|
||||||
|
Button(action: {
|
||||||
|
value += 0.10
|
||||||
|
value = round(value * 1000) / 1000 // Round to two decimal places
|
||||||
|
print(value)
|
||||||
|
}) {
|
||||||
|
Text("+")
|
||||||
|
.frame(width: 30, height: 30)
|
||||||
|
.background(Color.gray.opacity(0.2))
|
||||||
|
.cornerRadius(5)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension NumberFormatter {
|
||||||
|
static var floatFormatter: NumberFormatter {
|
||||||
|
let formatter = NumberFormatter()
|
||||||
|
formatter.numberStyle = .decimal
|
||||||
|
formatter.maximumFractionDigits = 2
|
||||||
|
formatter.minimumFractionDigits = 2
|
||||||
|
formatter.allowsFloats = true
|
||||||
|
return formatter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user