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",
|
||||
);
|
||||
MARKETING_VERSION = 1.0;
|
||||
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",
|
||||
);
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.stossy11.MeloNX;
|
||||
|
Binary file not shown.
@ -35,21 +35,5 @@
|
||||
</Actions>
|
||||
</BreakpointContent>
|
||||
</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>
|
||||
</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] = []
|
||||
|
||||
static let shared = Ryujinx()
|
||||
|
||||
private init() {}
|
||||
|
||||
public struct Configuration : Codable {
|
||||
var gamepath: String
|
||||
var inputids: [String]
|
||||
var resscale: Float
|
||||
var debuglogs: Bool
|
||||
var tracelogs: Bool
|
||||
var listinputids: Bool
|
||||
@ -56,11 +61,13 @@ class Ryujinx {
|
||||
listinputids: Bool = false,
|
||||
fullscreen: Bool = true,
|
||||
memoryManagerMode: String = "HostMapped",
|
||||
disableVSync: Bool = true,
|
||||
disableVSync: Bool = false,
|
||||
disableShaderCache: Bool = false,
|
||||
disableDockedMode: Bool = true,
|
||||
disableDockedMode: Bool = false,
|
||||
enableTextureRecompression: Bool = true,
|
||||
additionalArgs: [String] = []) {
|
||||
additionalArgs: [String] = [],
|
||||
resscale: Float = 1.00
|
||||
) {
|
||||
self.gamepath = gamepath
|
||||
self.inputids = inputids
|
||||
self.debuglogs = debuglogs
|
||||
@ -73,17 +80,18 @@ class Ryujinx {
|
||||
self.enableTextureRecompression = enableTextureRecompression
|
||||
self.additionalArgs = additionalArgs
|
||||
self.memoryManagerMode = memoryManagerMode
|
||||
self.resscale = resscale
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
func start(with config: Configuration) throws {
|
||||
guard !isRunning else {
|
||||
throw RyujinxError.alreadyRunning
|
||||
}
|
||||
|
||||
isRunning = true
|
||||
|
||||
// Start The Emulation on the main thread
|
||||
DispatchQueue.main.async {
|
||||
do {
|
||||
@ -139,6 +147,10 @@ class Ryujinx {
|
||||
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
|
||||
if config.disableVSync {
|
||||
args.append("--disable-vsync")
|
||||
|
@ -73,7 +73,7 @@ struct ContentView: View {
|
||||
GameListView(startemu: $game)
|
||||
.onAppear() {
|
||||
Timer.scheduledTimer(withTimeInterval: 1.0, repeats: false) { _ in
|
||||
controllersList = Ryujinx().getConnectedControllers()
|
||||
controllersList = Ryujinx.shared.getConnectedControllers()
|
||||
controllersList.removeAll(where: { $0.id == "0" })
|
||||
}
|
||||
}
|
||||
@ -88,7 +88,7 @@ struct ContentView: View {
|
||||
}
|
||||
Section("Controller") {
|
||||
Button {
|
||||
controllersList = Ryujinx().getConnectedControllers()
|
||||
controllersList = Ryujinx.shared.getConnectedControllers()
|
||||
controllersList.removeAll(where: { $0.id == "0" })
|
||||
} label: {
|
||||
Text("Refresh")
|
||||
@ -128,10 +128,12 @@ struct ContentView: View {
|
||||
allocateSixGB()
|
||||
|
||||
// Start the emulation
|
||||
|
||||
print("Is MetalHud Enabled? " + (MTLHud.shared.isEnabled ? "yeah" : "nope"))
|
||||
do {
|
||||
setupVirtualController()
|
||||
|
||||
try Ryujinx().start(with: config)
|
||||
try Ryujinx.shared.start(with: config)
|
||||
|
||||
|
||||
} catch {
|
||||
|
@ -17,47 +17,66 @@ struct SettingsView: View {
|
||||
("SoftwarePageTable", "Software")
|
||||
]
|
||||
|
||||
@AppStorage("MTL_HUD_ENABLED") var metalHUDEnabled: Bool = false
|
||||
|
||||
var body: some View {
|
||||
Form {
|
||||
Section(header: Text("Graphics and Performance")) {
|
||||
Toggle("Ryujinx Fullscreen", isOn: $config.fullscreen)
|
||||
Toggle("Disable V-Sync", isOn: $config.disableVSync)
|
||||
Toggle("Disable Shader Cache", isOn: $config.disableShaderCache)
|
||||
Toggle("Enable Texture Recompression", isOn: $config.enableTextureRecompression)
|
||||
}
|
||||
ScrollView {
|
||||
VStack {
|
||||
Section(header: Text("Graphics and Performance")) {
|
||||
Toggle("Ryujinx Fullscreen", isOn: $config.fullscreen)
|
||||
Toggle("Disable V-Sync", isOn: $config.disableVSync)
|
||||
Toggle("Disable Shader Cache", isOn: $config.disableShaderCache)
|
||||
Toggle("Enable Texture Recompression", isOn: $config.enableTextureRecompression)
|
||||
Resolution(value: $config.resscale)
|
||||
Toggle("Enable Metal HUD", isOn: $metalHUDEnabled)
|
||||
.onChange(of: metalHUDEnabled) { newValue in
|
||||
if newValue {
|
||||
MTLHud.shared.enable()
|
||||
} else {
|
||||
MTLHud.shared.disable()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Section(header: Text("Input Settings")) {
|
||||
Toggle("List Input IDs", isOn: $config.listinputids)
|
||||
// Toggle("Host Mapped Memory", isOn: $config.hostMappedMemory)
|
||||
Toggle("Disable Docked Mode", isOn: $config.disableDockedMode)
|
||||
}
|
||||
Section(header: Text("Input Settings")) {
|
||||
Toggle("List Input IDs", isOn: $config.listinputids)
|
||||
// Toggle("Host Mapped Memory", isOn: $config.hostMappedMemory)
|
||||
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")) {
|
||||
Picker("Memory Manager Mode", selection: $config.memoryManagerMode) {
|
||||
ForEach(memoryManagerModes, id: \.0) { key, displayName in
|
||||
Text(displayName).tag(key)
|
||||
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: ", ")
|
||||
},
|
||||
set: { newValue in
|
||||
config.additionalArgs = newValue.split(separator: ",").map { String($0).trimmingCharacters(in: .whitespaces) }
|
||||
}
|
||||
))
|
||||
|
||||
Section(header: Text("Additional Settings")) {
|
||||
//TextField("Game Path", text: $config.gamepath)
|
||||
|
||||
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 {
|
||||
if let configs = loadSettings() {
|
||||
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