diff --git a/src/ARMeilleure/Translation/Cache/JitCache.cs b/src/ARMeilleure/Translation/Cache/JitCache.cs index 1845f199d..0ff9645c9 100644 --- a/src/ARMeilleure/Translation/Cache/JitCache.cs +++ b/src/ARMeilleure/Translation/Cache/JitCache.cs @@ -15,11 +15,11 @@ namespace ARMeilleure.Translation.Cache static partial class JitCache { private static readonly int _pageSize = (int)MemoryBlock.GetPageSize(); - private static readonly int _pageMask = _pageSize - 2; + private static readonly int _pageMask = _pageSize - 1; private const int CodeAlignment = 4; // Bytes. private const int CacheSize = 2047 * 1024 * 1024; - private const int CacheSizeIOS = 64 * 1024 * 1024; + private const int CacheSizeIOS = 128 * 1024 * 1024; private static ReservedRegion _jitRegion; private static JitCacheInvalidation _jitCacheInvalidator; diff --git a/src/MeloNX/MeloNX.xcodeproj/project.pbxproj b/src/MeloNX/MeloNX.xcodeproj/project.pbxproj index b80341ed1..b7ed4fa70 100644 --- a/src/MeloNX/MeloNX.xcodeproj/project.pbxproj +++ b/src/MeloNX/MeloNX.xcodeproj/project.pbxproj @@ -528,6 +528,7 @@ "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", + "$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries", ); GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = MeloNX/Info.plist; @@ -668,6 +669,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 = 0.0.8; PRODUCT_BUNDLE_IDENTIFIER = com.stossy11.MeloNX; @@ -698,6 +703,7 @@ "$(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 = 3; GENERATE_INFOPLIST_FILE = YES; @@ -839,6 +845,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 = 0.0.8; PRODUCT_BUNDLE_IDENTIFIER = com.stossy11.MeloNX; diff --git a/src/MeloNX/MeloNX.xcodeproj/project.xcworkspace/xcuserdata/stossy11.xcuserdatad/UserInterfaceState.xcuserstate b/src/MeloNX/MeloNX.xcodeproj/project.xcworkspace/xcuserdata/stossy11.xcuserdatad/UserInterfaceState.xcuserstate index 2433ad0d8..6ec0cdc29 100644 Binary files a/src/MeloNX/MeloNX.xcodeproj/project.xcworkspace/xcuserdata/stossy11.xcuserdatad/UserInterfaceState.xcuserstate and b/src/MeloNX/MeloNX.xcodeproj/project.xcworkspace/xcuserdata/stossy11.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/src/MeloNX/MeloNX/Core/Headers/Ryujinx-Header.h b/src/MeloNX/MeloNX/Core/Headers/Ryujinx-Header.h index fb12df14a..8bcc7930d 100644 --- a/src/MeloNX/MeloNX/Core/Headers/Ryujinx-Header.h +++ b/src/MeloNX/MeloNX/Core/Headers/Ryujinx-Header.h @@ -35,6 +35,8 @@ void stop_emulation(); int main_ryujinx_sdl(int argc, char **argv); +int get_current_fps(); + void initialize(); const char* get_game_controllers(); diff --git a/src/MeloNX/MeloNX/Core/Swift/Controller/WaitforVC.swift b/src/MeloNX/MeloNX/Core/Swift/Controller/WaitforVC.swift index 43cf638f7..d872e91a9 100644 --- a/src/MeloNX/MeloNX/Core/Swift/Controller/WaitforVC.swift +++ b/src/MeloNX/MeloNX/Core/Swift/Controller/WaitforVC.swift @@ -44,7 +44,8 @@ func waitforcontroller() { Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { timer in if findGCControllerView(in: window) == nil { window.addSubview(containerView) - + } else { + timer.invalidate() } window.bringSubviewToFront(containerView) diff --git a/src/MeloNX/MeloNX/Core/Swift/Display/FPSDisplay/FPSMonitor.swift b/src/MeloNX/MeloNX/Core/Swift/Display/FPSDisplay/FPSMonitor.swift new file mode 100644 index 000000000..bbd8bfae4 --- /dev/null +++ b/src/MeloNX/MeloNX/Core/Swift/Display/FPSDisplay/FPSMonitor.swift @@ -0,0 +1,41 @@ +// +// FPSMonitor.swift +// MeloNX +// +// Created by Stossy11 on 21/12/2024. +// + +import Foundation +import SwiftUI + +class FPSMonitor: ObservableObject { + @Published private(set) var currentFPS: UInt64 = 0 + private var timer: Timer? + + init() { + timer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { [weak self] _ in + self?.updateFPS() + } + } + + deinit { + timer?.invalidate() + } + + private func updateFPS() { + let currentfps = UInt64(get_current_fps()) + + self.currentFPS = currentfps + } + + + func formatFPS() -> String { + let fps = Double(currentFPS) + let fpsString = String(format: "FPS: %.2f", fps) + + return fpsString + } +} + + + diff --git a/src/MeloNX/MeloNX/Core/Swift/Display/MemoryDisplay/MemoryUsageMonitor.swift b/src/MeloNX/MeloNX/Core/Swift/Display/MemoryDisplay/MemoryUsageMonitor.swift new file mode 100644 index 000000000..06070a38b --- /dev/null +++ b/src/MeloNX/MeloNX/Core/Swift/Display/MemoryDisplay/MemoryUsageMonitor.swift @@ -0,0 +1,52 @@ +// +// MemoryUsageMonitor.swift +// MeloNX +// +// Created by Stossy11 on 21/12/2024. +// + +import Foundation +import SwiftUI + +class MemoryUsageMonitor: ObservableObject { + @Published private(set) var memoryUsage: UInt64 = 0 + private var timer: Timer? + + init() { + timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] _ in + self?.updateMemoryUsage() + } + } + + deinit { + timer?.invalidate() + } + + private func updateMemoryUsage() { + var taskInfo = task_vm_info_data_t() + var count = mach_msg_type_number_t(MemoryLayout.size) / 4 + let result: kern_return_t = withUnsafeMutablePointer(to: &taskInfo) { + $0.withMemoryRebound(to: integer_t.self, capacity: 1) { + task_info(mach_task_self_, task_flavor_t(TASK_VM_INFO), $0, &count) + } + } + + if result == KERN_SUCCESS { + memoryUsage = taskInfo.phys_footprint + } + else { + print("Error with task_info(): " + + (String(cString: mach_error_string(result), encoding: String.Encoding.ascii) ?? "unknown error")) + } + } + + func formatMemorySize(_ bytes: UInt64) -> String { + let formatter = ByteCountFormatter() + formatter.allowedUnits = [.useMB, .useGB] + formatter.countStyle = .memory + return formatter.string(fromByteCount: Int64(bytes)) + } + +} + + diff --git a/src/MeloNX/MeloNX/Core/Swift/Display/PerformanceDisplay/PerformanceOverlay.swift b/src/MeloNX/MeloNX/Core/Swift/Display/PerformanceDisplay/PerformanceOverlay.swift new file mode 100644 index 000000000..ac014ff4a --- /dev/null +++ b/src/MeloNX/MeloNX/Core/Swift/Display/PerformanceDisplay/PerformanceOverlay.swift @@ -0,0 +1,22 @@ +// +// Untitled.swift +// MeloNX +// +// Created by Stossy11 on 21/12/2024. +// + +import SwiftUI + +struct PerformanceOverlayView: View { + @StateObject private var memorymonitor = MemoryUsageMonitor() + + @StateObject private var fpsmonitor = FPSMonitor() + + var body: some View { + VStack { + Text("\(fpsmonitor.formatFPS())") + Text(memorymonitor.formatMemorySize(memorymonitor.memoryUsage)) + } + } +} + diff --git a/src/MeloNX/MeloNX/MeloNX.entitlements b/src/MeloNX/MeloNX/MeloNX.entitlements index aff1d9915..99f471672 100644 --- a/src/MeloNX/MeloNX/MeloNX.entitlements +++ b/src/MeloNX/MeloNX/MeloNX.entitlements @@ -2,10 +2,6 @@ - com.apple.developer.kernel.extended-virtual-addressing - - com.apple.developer.kernel.increased-debugging-memory-limit - com.apple.developer.kernel.increased-memory-limit diff --git a/src/MeloNX/MeloNX/Views/ContentView.swift b/src/MeloNX/MeloNX/Views/ContentView.swift index 9365efe5c..0a21f33c3 100644 --- a/src/MeloNX/MeloNX/Views/ContentView.swift +++ b/src/MeloNX/MeloNX/Views/ContentView.swift @@ -62,8 +62,7 @@ struct ContentView: View { emulationView .onAppear() { Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { timer in - print(quit) - + timer.invalidate() quits = quit if quits { diff --git a/src/MeloNX/MeloNX/Views/ControllerView/ControllerView.swift b/src/MeloNX/MeloNX/Views/ControllerView/ControllerView.swift index c760f8154..54b260e8f 100644 --- a/src/MeloNX/MeloNX/Views/ControllerView/ControllerView.swift +++ b/src/MeloNX/MeloNX/Views/ControllerView/ControllerView.swift @@ -11,23 +11,29 @@ import SwiftUIJoystick import CoreMotion struct ControllerView: View { + + @AppStorage("performacehud") var performacehud: Bool = false @AppStorage("quit") var quit: Bool = false var body: some View { GeometryReader { geometry in if geometry.size.height > geometry.size.width && UIDevice.current.userInterfaceIdiom != .pad { VStack { - /* - HStack { - Spacer() - - Button("Stop emulation") { - DispatchQueue.main.async { - stop_emulation() - quit = true - } + if performacehud { + HStack { + + PerformanceOverlayView() + + Spacer() + + // Button("Stop emulation") { + // DispatchQueue.main.async { + // stop_emulation() + // quit = true + // } + // } } } - */ + Spacer() VStack { @@ -62,18 +68,21 @@ struct ControllerView: View { } else { // could be landscape VStack { - /* - HStack { - Spacer() - - Button("Stop emulation") { - DispatchQueue.main.async { - stop_emulation() - quit = true - } + if performacehud { + HStack { + PerformanceOverlayView() + + Spacer() + + // Button("Stop emulation") { + // DispatchQueue.main.async { + // stop_emulation() + // quit = true + // } + // } } } - */ + Spacer() VStack { diff --git a/src/MeloNX/MeloNX/Views/SettingsView/SettingsView.swift b/src/MeloNX/MeloNX/Views/SettingsView/SettingsView.swift index bb23f964e..ab3746528 100644 --- a/src/MeloNX/MeloNX/Views/SettingsView/SettingsView.swift +++ b/src/MeloNX/MeloNX/Views/SettingsView/SettingsView.swift @@ -29,6 +29,8 @@ struct SettingsView: View { @AppStorage("RyuDemoControls") var ryuDemo: Bool = false @AppStorage("MTL_HUD_ENABLED") var metalHUDEnabled: Bool = false + @AppStorage("performacehud") var performacehud: Bool = false + @State private var showResolutionInfo = false @State private var searchText = "" @@ -102,18 +104,10 @@ struct SettingsView: View { } .padding(.vertical, 8) - Toggle(isOn: $metalHUDEnabled) { - labelWithIcon("Metal HUD", iconName: "speedometer") + Toggle(isOn: $performacehud) { + labelWithIcon("Performance Overlay", iconName: "speedometer") } .tint(.blue) - .onChange(of: metalHUDEnabled) { newValue in - // Preserves original functionality - if newValue { - MTLHud.shared.enable() - } else { - MTLHud.shared.disable() - } - } } header: { Text("Graphics & Performance") .font(.title3.weight(.semibold)) @@ -215,27 +209,7 @@ struct SettingsView: View { } footer: { Text("Configure input devices and on-screen controls for easier navigation and play.") } - - // Logging - Section { - Toggle(isOn: $config.debuglogs) { - labelWithIcon("Debug Logs", iconName: "exclamationmark.bubble") - } - .tint(.blue) - - Toggle(isOn: $config.tracelogs) { - labelWithIcon("Trace Logs", iconName: "waveform.path") - } - .tint(.blue) - } header: { - Text("Logging") - .font(.title3.weight(.semibold)) - .textCase(nil) - .headerProminence(.increased) - } footer: { - Text("Enable logs for troubleshooting or keep them off for a cleaner experience.") - } - + // CPU Mode Section { if filteredMemoryModes.isEmpty { @@ -259,13 +233,36 @@ struct SettingsView: View { Text("Select how memory is managed. 'Host (fast)' is best for most users.") } + + // Other Settings + Section { + + Toggle(isOn: $useTrollStore) { + labelWithIcon("TrollStore", iconName: "troll.svg") + } + .tint(.blue) + + Toggle(isOn: $config.debuglogs) { + labelWithIcon("Debug Logs", iconName: "exclamationmark.bubble") + } + .tint(.blue) + + Toggle(isOn: $config.tracelogs) { + labelWithIcon("Trace Logs", iconName: "waveform.path") + } + .tint(.blue) + } header: { + Text("Miscellaneous Options") + .font(.title3.weight(.semibold)) + .textCase(nil) + .headerProminence(.increased) + } footer: { + Text("Enable logs for troubleshooting and Enable automatic TrollStore JIT.") + } + // Advanced Section { DisclosureGroup { - Toggle(isOn: $useTrollStore) { - labelWithIcon("TrollStore", iconName: "troll.svg") - } - .tint(.blue) HStack { labelWithIcon("Page Size", iconName: "textformat.size") @@ -277,7 +274,7 @@ struct SettingsView: View { TextField("Additional Arguments", text: Binding( get: { - config.additionalArgs.joined(separator: ", ") + config.additionalArgs.joined(separator: " ") }, set: { newValue in config.additionalArgs = newValue diff --git a/src/Ryujinx.Ava/AppHost.cs b/src/Ryujinx.Ava/AppHost.cs index 2fd9ce00d..cdda2d109 100644 --- a/src/Ryujinx.Ava/AppHost.cs +++ b/src/Ryujinx.Ava/AppHost.cs @@ -51,6 +51,59 @@ using System.Diagnostics; using System.IO; using System.Threading; using System.Threading.Tasks; +using ARMeilleure.Translation; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Input; +using Avalonia.Threading; +using LibHac.Tools.FsSystem; +using Ryujinx.Audio.Backends.Dummy; +using Ryujinx.Audio.Backends.OpenAL; +using Ryujinx.Audio.Backends.SDL2; +using Ryujinx.Audio.Backends.SoundIo; +using Ryujinx.Audio.Integration; +using Ryujinx.Ava.Common; +using Ryujinx.Ava.Common.Locale; +using Ryujinx.Ava.Input; +using Ryujinx.Ava.UI.Helpers; +using Ryujinx.Ava.UI.Models; +using Ryujinx.Ava.UI.Renderer; +using Ryujinx.Ava.UI.ViewModels; +using Ryujinx.Ava.UI.Windows; +using Ryujinx.Common; +using Ryujinx.Common.Configuration; +using Ryujinx.Common.Configuration.Multiplayer; +using Ryujinx.Common.Logging; +using Ryujinx.Common.SystemInterop; +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.GAL.Multithreading; +using Ryujinx.Graphics.Gpu; +using Ryujinx.Graphics.OpenGL; +using Ryujinx.Graphics.Vulkan; +using Ryujinx.HLE; +using Ryujinx.HLE.FileSystem; +using Ryujinx.HLE.HOS; +using Ryujinx.HLE.HOS.Services.Account.Acc; +using Ryujinx.HLE.HOS.SystemState; +using Ryujinx.Input; +using Ryujinx.Input.HLE; +using Ryujinx.Ui.App.Common; +using Ryujinx.Ui.Common; +using Ryujinx.Ui.Common.Configuration; +using Ryujinx.Ui.Common.Helper; +using Silk.NET.Vulkan; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Formats.Png; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SPB.Graphics.Vulkan; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Threading; +using System.Threading.Tasks; using static Ryujinx.Ava.UI.Helpers.Win32NativeInterop; using AntiAliasing = Ryujinx.Common.Configuration.AntiAliasing; using Image = SixLabors.ImageSharp.Image; diff --git a/src/Ryujinx.Graphics.Gpu/Ryujinx.Graphics.Gpu.csproj b/src/Ryujinx.Graphics.Gpu/Ryujinx.Graphics.Gpu.csproj index 6f1cce6ac..8cb448098 100644 --- a/src/Ryujinx.Graphics.Gpu/Ryujinx.Graphics.Gpu.csproj +++ b/src/Ryujinx.Graphics.Gpu/Ryujinx.Graphics.Gpu.csproj @@ -1,4 +1,4 @@ - + net8.0 diff --git a/src/Ryujinx.Headless.SDL2/Program.cs b/src/Ryujinx.Headless.SDL2/Program.cs index 22b1b2b23..a42b5bc0f 100644 --- a/src/Ryujinx.Headless.SDL2/Program.cs +++ b/src/Ryujinx.Headless.SDL2/Program.cs @@ -147,6 +147,21 @@ namespace Ryujinx.Headless.SDL2 return 0; } + + [UnmanagedCallersOnly(EntryPoint = "get_current_fps")] + public static unsafe int GetFPS() + { + if (_window != null) { + Switch Device = _window.Device; + + int intValue = (int)Device.Statistics.GetGameFrameRate(); + + return intValue; + } + return 0; + + } + [UnmanagedCallersOnly(EntryPoint = "initialize")] public static unsafe void Initialize() { diff --git a/src/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj b/src/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj index e6cfbda96..25831c6de 100644 --- a/src/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj +++ b/src/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj @@ -8,7 +8,7 @@ $(DefineConstants);$(ExtraDefineConstants) - true - + true true true diff --git a/src/Ryujinx.Memory/MachJitWorkaround.cs b/src/Ryujinx.Memory/MachJitWorkaround.cs index cfb1e419c..ecffb70e1 100644 --- a/src/Ryujinx.Memory/MachJitWorkaround.cs +++ b/src/Ryujinx.Memory/MachJitWorkaround.cs @@ -7,6 +7,7 @@ namespace Ryujinx.Memory [SupportedOSPlatform("ios")] static unsafe partial class MachJitWorkaround { + // Previous imports remain the same [LibraryImport("libc")] public static partial int mach_task_self(); @@ -28,141 +29,206 @@ namespace Ryujinx.Memory [LibraryImport("libc")] public static partial int vm_remap(IntPtr target_task, IntPtr* target_address, IntPtr size, IntPtr mask, int flags, IntPtr src_task, IntPtr src_address, int copy, int* cur_protection, int* max_protection, int inheritance); - const int MAP_MEM_LEDGER_TAGGED = 0x002000; - const int MAP_MEM_NAMED_CREATE = 0x020000; - - const int VM_PROT_READ = 0x01; - const int VM_PROT_WRITE = 0x02; - const int VM_PROT_EXECUTE = 0x04; - - const int VM_LEDGER_TAG_DEFAULT = 0x00000001; - const int VM_LEDGER_FLAG_NO_FOOTPRINT = 0x00000001; - - const int VM_INHERIT_COPY = 1; - const int VM_INHERIT_DEFAULT = VM_INHERIT_COPY; - - const int VM_FLAGS_FIXED = 0x0000; - const int VM_FLAGS_ANYWHERE = 0x0001; - const int VM_FLAGS_OVERWRITE = 0x4000; - - const IntPtr TASK_NULL = 0; - - public static void ReallocateBlock(IntPtr address, int size) + private static class Flags { - IntPtr selfTask = mach_task_self(); - IntPtr memorySize = (IntPtr)size; - IntPtr memoryObjectPort = IntPtr.Zero; + public const int MAP_MEM_LEDGER_TAGGED = 0x002000; + public const int MAP_MEM_NAMED_CREATE = 0x020000; + public const int VM_PROT_READ = 0x01; + public const int VM_PROT_WRITE = 0x02; + public const int VM_PROT_EXECUTE = 0x04; + public const int VM_LEDGER_TAG_DEFAULT = 0x00000001; + public const int VM_LEDGER_FLAG_NO_FOOTPRINT = 0x00000001; + public const int VM_INHERIT_COPY = 1; + public const int VM_INHERIT_DEFAULT = VM_INHERIT_COPY; + public const int VM_FLAGS_FIXED = 0x0000; + public const int VM_FLAGS_ANYWHERE = 0x0001; + public const int VM_FLAGS_OVERWRITE = 0x4000; + } - int err = mach_make_memory_entry_64(selfTask, &memorySize, 0, MAP_MEM_NAMED_CREATE | MAP_MEM_LEDGER_TAGGED | VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE, &memoryObjectPort, 0); + private const IntPtr TASK_NULL = 0; + private static readonly IntPtr _selfTask; + + // Updated to iOS 16KB page size + private const int PAGE_SIZE = 16 * 1024; + private const ulong PAGE_MASK = ~((ulong)PAGE_SIZE - 1); - if (err != 0) + static MachJitWorkaround() + { + _selfTask = mach_task_self(); + } + + private static void HandleMachError(int error, string operation) + { + if (error != 0) { - throw new InvalidOperationException($"Make memory entry failed: {err}"); + throw new InvalidOperationException($"Mach operation '{operation}' failed with error: {error}"); } + } + + private static IntPtr ReallocateBlock(IntPtr address, int size) + { + // Ensure size is page-aligned + int alignedSize = (int)((((ulong)size + PAGE_SIZE - 1) & PAGE_MASK)); + + // Deallocate existing mapping + vm_deallocate(_selfTask, address, (IntPtr)alignedSize); + + IntPtr memorySize = (IntPtr)alignedSize; + IntPtr memoryObjectPort = IntPtr.Zero; try { - if (memorySize != (IntPtr)size) - { - throw new InvalidOperationException($"Created with size {memorySize} instead of {size}."); - } + // Create minimal permission memory entry initially + HandleMachError( + mach_make_memory_entry_64( + _selfTask, + &memorySize, + IntPtr.Zero, + Flags.MAP_MEM_NAMED_CREATE | Flags.MAP_MEM_LEDGER_TAGGED | + Flags.VM_PROT_READ | Flags.VM_PROT_WRITE, // Don't request execute initially + &memoryObjectPort, + IntPtr.Zero), + "make_memory_entry_64"); - err = mach_memory_entry_ownership(memoryObjectPort, TASK_NULL, VM_LEDGER_TAG_DEFAULT, VM_LEDGER_FLAG_NO_FOOTPRINT); - - if (err != 0) - { - throw new InvalidOperationException($"Failed to set ownership: {err}"); - } + // Set no-footprint flag to minimize memory usage + HandleMachError( + mach_memory_entry_ownership( + memoryObjectPort, + TASK_NULL, + Flags.VM_LEDGER_TAG_DEFAULT, + Flags.VM_LEDGER_FLAG_NO_FOOTPRINT), + "memory_entry_ownership"); IntPtr mapAddress = address; - err = vm_map( - selfTask, + // Map with minimal initial permissions + int result = vm_map( + _selfTask, &mapAddress, memorySize, - /*mask=*/ 0, - /*flags=*/ VM_FLAGS_OVERWRITE, + IntPtr.Zero, + Flags.VM_FLAGS_OVERWRITE, memoryObjectPort, - /*offset=*/ 0, - /*copy=*/ 0, - VM_PROT_READ | VM_PROT_WRITE, - VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE, - VM_INHERIT_COPY); + IntPtr.Zero, + 0, + Flags.VM_PROT_READ | Flags.VM_PROT_WRITE, + Flags.VM_PROT_READ | Flags.VM_PROT_WRITE | Flags.VM_PROT_EXECUTE, // Allow execute as max protection + Flags.VM_INHERIT_COPY); - if (err != 0) - { - throw new InvalidOperationException($"Failed to map: {err}"); - } + HandleMachError(result, "vm_map"); if (address != mapAddress) { - throw new InvalidOperationException($"Remap changed address"); + throw new InvalidOperationException("Memory mapping address mismatch"); } + + return mapAddress; } finally { - //mach_port_deallocate(selfTask, memoryObjectPort); + if (memoryObjectPort != IntPtr.Zero) + { + // Implement proper cleanup if needed + // mach_port_deallocate(_selfTask, memoryObjectPort); + } } - - Console.WriteLine($"Reallocated an area... {address:x16}"); } public static void ReallocateAreaWithOwnership(IntPtr address, int size) { - int mapChunkSize = 128 * 1024 * 1024; - IntPtr endAddress = address + size; - IntPtr blockAddress = address; - while (blockAddress < endAddress) + if (size <= 0) { - int blockSize = Math.Min(mapChunkSize, (int)(endAddress - blockAddress)); + throw new ArgumentException("Size must be positive", nameof(size)); + } - ReallocateBlock(blockAddress, blockSize); - - blockAddress += blockSize; + // Align size to 16KB page boundary + int alignedSize = (int)((((ulong)size + PAGE_SIZE - 1) & PAGE_MASK)); + + try + { + ReallocateBlock(address, alignedSize); + } + catch (InvalidOperationException) + { + // If first attempt fails, try with explicit deallocation and retry + vm_deallocate(_selfTask, address, (IntPtr)alignedSize); + ReallocateBlock(address, alignedSize); } } public static IntPtr AllocateSharedMemory(ulong size, bool reserve) { - IntPtr address = 0; - - int err = vm_allocate(mach_task_self(), &address, (IntPtr)size, VM_FLAGS_ANYWHERE); - - if (err != 0) + if (size == 0) { - throw new InvalidOperationException($"Failed to allocate shared memory: {err}"); + throw new ArgumentException("Size must be positive", nameof(size)); } + ulong alignedSize = (size + (ulong)PAGE_SIZE - 1) & PAGE_MASK; + + IntPtr address = IntPtr.Zero; + HandleMachError( + vm_allocate( + _selfTask, + &address, + (IntPtr)alignedSize, + Flags.VM_FLAGS_ANYWHERE), + "vm_allocate"); + return address; } public static void DestroySharedMemory(IntPtr handle, ulong size) { - vm_deallocate(mach_task_self(), handle, (IntPtr)size); + if (handle != IntPtr.Zero && size > 0) + { + ulong alignedSize = (size + (ulong)PAGE_SIZE - 1) & PAGE_MASK; + vm_deallocate(_selfTask, handle, (IntPtr)alignedSize); + } } public static IntPtr MapView(IntPtr sharedMemory, ulong srcOffset, IntPtr location, ulong size) { - IntPtr taskSelf = mach_task_self(); - IntPtr srcAddress = (IntPtr)((ulong)sharedMemory + srcOffset); - IntPtr dstAddress = location; - - int cur_protection = 0; - int max_protection = 0; - - int err = vm_remap(taskSelf, &dstAddress, (IntPtr)size, 0, VM_FLAGS_OVERWRITE, taskSelf, srcAddress, 0, &cur_protection, &max_protection, VM_INHERIT_DEFAULT); - - if (err != 0) + if (size == 0 || sharedMemory == IntPtr.Zero) { - throw new InvalidOperationException($"Failed to allocate remap memory: {err}"); + throw new ArgumentException("Invalid mapping parameters"); } + ulong alignedOffset = srcOffset & PAGE_MASK; + ulong alignedSize = (size + (ulong)PAGE_SIZE - 1) & PAGE_MASK; + + IntPtr srcAddress = (IntPtr)((ulong)sharedMemory + alignedOffset); + IntPtr dstAddress = location; + int curProtection = 0; + int maxProtection = 0; + + // Deallocate existing mapping + vm_deallocate(_selfTask, location, (IntPtr)alignedSize); + + HandleMachError( + vm_remap( + _selfTask, + &dstAddress, + (IntPtr)alignedSize, + IntPtr.Zero, + Flags.VM_FLAGS_FIXED, + _selfTask, + srcAddress, + 0, + &curProtection, + &maxProtection, + Flags.VM_INHERIT_DEFAULT), + "vm_remap"); + return dstAddress; } public static void UnmapView(IntPtr location, ulong size) { - vm_deallocate(mach_task_self(), location, (IntPtr)size); + if (location != IntPtr.Zero && size > 0) + { + ulong alignedSize = (size + (ulong)PAGE_SIZE - 1) & PAGE_MASK; + vm_deallocate(_selfTask, location, (IntPtr)alignedSize); + } } } } \ No newline at end of file