Add MetalHUD, Resolution Controls, change default settings

This commit is contained in:
Stossy11 2024-11-27 07:53:33 +11:00
parent 45e2785e93
commit a14aadf878
7 changed files with 194 additions and 58 deletions

View File

@ -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;

View File

@ -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>

View 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)
}
}

View File

@ -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")

View File

@ -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 {

View File

@ -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
}
}