Compare commits

..

3 Commits

Author SHA1 Message Date
riperiperi
50d7ecf76d Add alternative "GL" enum values for StencilOp (#3321)
This PR adds the alternative enum values for StencilOp. Similar to the other enums, I added these with the same names but with Gl added to the end. These are used by homebrew using Nouveau, though they might be used by games with the official Vulkan driver.

39d90be897/rnndb/graph/nv_3ddefs.xml (L77)

Fixes some broken graphics in Citra, such as missing shadows in Mario Kart 7. Likely fixes other homebrew.
2022-05-05 21:16:58 +02:00
gdkchan
42a2a80b87 Enable JIT service LLE (#2959)
* Enable JIT service LLE

* Force disable PPTC when using the JIT service

PPTC does not support multiple guest processes

* Fix build

* Make SM service registration per emulation context rather than global

* Address PR feedback
2022-05-05 15:23:30 -03:00
gdkchan
54deded929 Fix shared memory leak on Windows (#3319)
* Fix shared memory leak on Windows

* Fix memory leak caused by RO session disposal not decrementing the memory manager ref count

* Fix UnmapViewInternal deadlock

* Was not supposed to add those back
2022-05-05 14:58:59 -03:00
15 changed files with 473 additions and 91 deletions

View File

@@ -9,6 +9,15 @@ namespace Ryujinx.Graphics.GAL
DecrementAndClamp,
Invert,
IncrementAndWrap,
DecrementAndWrap
DecrementAndWrap,
ZeroGl = 0x0,
InvertGl = 0x150a,
KeepGl = 0x1e00,
ReplaceGl = 0x1e01,
IncrementAndClampGl = 0x1e02,
DecrementAndClampGl = 0x1e03,
IncrementAndWrapGl = 0x8507,
DecrementAndWrapGl = 0x8508
}
}

View File

@@ -379,20 +379,28 @@ namespace Ryujinx.Graphics.OpenGL
switch (op)
{
case GAL.StencilOp.Keep:
case GAL.StencilOp.KeepGl:
return OpenTK.Graphics.OpenGL.StencilOp.Keep;
case GAL.StencilOp.Zero:
case GAL.StencilOp.ZeroGl:
return OpenTK.Graphics.OpenGL.StencilOp.Zero;
case GAL.StencilOp.Replace:
case GAL.StencilOp.ReplaceGl:
return OpenTK.Graphics.OpenGL.StencilOp.Replace;
case GAL.StencilOp.IncrementAndClamp:
case GAL.StencilOp.IncrementAndClampGl:
return OpenTK.Graphics.OpenGL.StencilOp.Incr;
case GAL.StencilOp.DecrementAndClamp:
case GAL.StencilOp.DecrementAndClampGl:
return OpenTK.Graphics.OpenGL.StencilOp.Decr;
case GAL.StencilOp.Invert:
case GAL.StencilOp.InvertGl:
return OpenTK.Graphics.OpenGL.StencilOp.Invert;
case GAL.StencilOp.IncrementAndWrap:
case GAL.StencilOp.IncrementAndWrapGl:
return OpenTK.Graphics.OpenGL.StencilOp.IncrWrap;
case GAL.StencilOp.DecrementAndWrap:
case GAL.StencilOp.DecrementAndWrapGl:
return OpenTK.Graphics.OpenGL.StencilOp.DecrWrap;
}

View File

@@ -24,6 +24,7 @@ using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using static LibHac.Fs.ApplicationSaveDataManagement;
using static Ryujinx.HLE.HOS.ModLoader;
@@ -308,6 +309,94 @@ namespace Ryujinx.HLE.HOS
LoadNca(nca, null, null);
}
public void LoadServiceNca(string ncaFile)
{
// Disable PPTC here as it does not support multiple processes running.
// TODO: This should be eventually removed and it should stop using global state and
// instead manage the cache per process.
Ptc.Close();
PtcProfiler.Stop();
FileStream file = new FileStream(ncaFile, FileMode.Open, FileAccess.Read);
Nca mainNca = new Nca(_device.Configuration.VirtualFileSystem.KeySet, file.AsStorage(false));
if (mainNca.Header.ContentType != NcaContentType.Program)
{
Logger.Error?.Print(LogClass.Loader, "Selected NCA is not a \"Program\" NCA");
return;
}
IFileSystem codeFs = null;
if (mainNca.CanOpenSection(NcaSectionType.Code))
{
codeFs = mainNca.OpenFileSystem(NcaSectionType.Code, _device.System.FsIntegrityCheckLevel);
}
if (codeFs == null)
{
Logger.Error?.Print(LogClass.Loader, "No ExeFS found in NCA");
return;
}
using var npdmFile = new UniqueRef<IFile>();
Result result = codeFs.OpenFile(ref npdmFile.Ref(), "/main.npdm".ToU8Span(), OpenMode.Read);
MetaLoader metaData;
npdmFile.Get.GetSize(out long fileSize).ThrowIfFailure();
var npdmBuffer = new byte[fileSize];
npdmFile.Get.Read(out _, 0, npdmBuffer).ThrowIfFailure();
metaData = new MetaLoader();
metaData.Load(npdmBuffer).ThrowIfFailure();
NsoExecutable[] nsos = new NsoExecutable[ExeFsPrefixes.Length];
for (int i = 0; i < nsos.Length; i++)
{
string name = ExeFsPrefixes[i];
if (!codeFs.FileExists($"/{name}"))
{
continue; // File doesn't exist, skip.
}
Logger.Info?.Print(LogClass.Loader, $"Loading {name}...");
using var nsoFile = new UniqueRef<IFile>();
codeFs.OpenFile(ref nsoFile.Ref(), $"/{name}".ToU8Span(), OpenMode.Read).ThrowIfFailure();
nsos[i] = new NsoExecutable(nsoFile.Release().AsStorage(), name);
}
// Collect the nsos, ignoring ones that aren't used.
NsoExecutable[] programs = nsos.Where(x => x != null).ToArray();
MemoryManagerMode memoryManagerMode = _device.Configuration.MemoryManagerMode;
if (!MemoryBlock.SupportsFlags(MemoryAllocationFlags.ViewCompatible))
{
memoryManagerMode = MemoryManagerMode.SoftwarePageTable;
}
metaData.GetNpdm(out Npdm npdm).ThrowIfFailure();
ProgramInfo programInfo = new ProgramInfo(in npdm, allowCodeMemoryForJit: false);
ProgramLoader.LoadNsos(_device.System.KernelContext, out _, metaData, programInfo, executables: programs);
string titleIdText = npdm.Aci.Value.ProgramId.Value.ToString("x16");
bool titleIs64Bit = (npdm.Meta.Value.Flags & 1) != 0;
string programName = Encoding.ASCII.GetString(npdm.Meta.Value.ProgramName).TrimEnd('\0');
Logger.Info?.Print(LogClass.Loader, $"Service Loaded: {programName} [{titleIdText}] [{(titleIs64Bit ? "64-bit" : "32-bit")}]");
}
private void LoadNca(Nca mainNca, Nca patchNca, Nca controlNca)
{
if (mainNca.Header.ContentType != NcaContentType.Program)
@@ -508,7 +597,7 @@ namespace Ryujinx.HLE.HOS
{
if (_device.Configuration.VirtualFileSystem.ModLoader.ReplaceExefsPartition(TitleId, ref codeFs))
{
metaData = null; //TODO: Check if we should retain old npdm
metaData = null; // TODO: Check if we should retain old npdm.
}
metaData ??= ReadNpdm(codeFs);
@@ -521,7 +610,7 @@ namespace Ryujinx.HLE.HOS
if (!codeFs.FileExists($"/{name}"))
{
continue; // file doesn't exist, skip
continue; // File doesn't exist, skip.
}
Logger.Info?.Print(LogClass.Loader, $"Loading {name}...");
@@ -533,13 +622,13 @@ namespace Ryujinx.HLE.HOS
nsos[i] = new NsoExecutable(nsoFile.Release().AsStorage(), name);
}
// ExeFs file replacements
// ExeFs file replacements.
ModLoadResult modLoadResult = _device.Configuration.VirtualFileSystem.ModLoader.ApplyExefsMods(TitleId, nsos);
// collect the nsos, ignoring ones that aren't used
// Collect the nsos, ignoring ones that aren't used.
NsoExecutable[] programs = nsos.Where(x => x != null).ToArray();
// take the npdm from mods if present
// Take the npdm from mods if present.
if (modLoadResult.Npdm != null)
{
metaData = modLoadResult.Npdm;
@@ -598,7 +687,7 @@ namespace Ryujinx.HLE.HOS
executable = obj;
// homebrew NRO can actually have some data after the actual NRO
// Homebrew NRO can actually have some data after the actual NRO.
if (input.Length > obj.FileSize)
{
input.Position = obj.FileSize;
@@ -677,7 +766,7 @@ namespace Ryujinx.HLE.HOS
TitleIs64Bit = (npdm.Meta.Value.Flags & 1) != 0;
_device.System.LibHacHorizonManager.ArpIReader.ApplicationId = new LibHac.ApplicationId(TitleId);
// Explicitly null titleid to disable the shader cache
// Explicitly null titleid to disable the shader cache.
Graphics.Gpu.GraphicsConfig.TitleId = null;
_device.Gpu.HostInitalized.Set();

View File

@@ -72,6 +72,8 @@ namespace Ryujinx.HLE.HOS
internal List<NfpDevice> NfpDevices { get; private set; }
internal SmRegistry SmRegistry { get; private set; }
internal ServerBase SmServer { get; private set; }
internal ServerBase BsdServer { get; private set; }
internal ServerBase AudRenServer { get; private set; }
@@ -291,7 +293,8 @@ namespace Ryujinx.HLE.HOS
private void InitializeServices()
{
SmServer = new ServerBase(KernelContext, "SmServer", () => new IUserInterface(KernelContext));
SmRegistry = new SmRegistry();
SmServer = new ServerBase(KernelContext, "SmServer", () => new IUserInterface(KernelContext, SmRegistry));
// Wait until SM server thread is done with initialization,
// only then doing connections to SM is safe.
@@ -468,7 +471,7 @@ namespace Ryujinx.HLE.HOS
AudioRendererManager.Dispose();
LibHacHorizonManager.PmClient.Fs.UnregisterProgram(LibHacHorizonManager.ApplicationClient.Os.GetCurrentProcessId().Value).ThrowIfFailure();
KernelContext.Dispose();
}
}

View File

@@ -2,9 +2,12 @@ using LibHac;
using LibHac.Account;
using LibHac.Common;
using LibHac.Fs;
using LibHac.FsSystem;
using LibHac.Ncm;
using LibHac.Ns;
using Ryujinx.Common;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.Exceptions;
using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.HLE.HOS.Kernel.Memory;
@@ -12,9 +15,11 @@ using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.HLE.HOS.Services.Am.AppletAE.Storage;
using Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.ApplicationProxy.Types;
using Ryujinx.HLE.HOS.Services.Sdb.Pdm.QueryService;
using Ryujinx.HLE.HOS.Services.Sm;
using Ryujinx.HLE.HOS.SystemState;
using System;
using System.Numerics;
using System.Threading;
using static LibHac.Fs.ApplicationSaveDataManagement;
using AccountUid = Ryujinx.HLE.HOS.Services.Account.Acc.UserId;
@@ -37,6 +42,8 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati
private int _notificationStorageChannelEventHandle;
private int _healthWarningDisappearedSystemEventHandle;
private int _jitLoaded;
private HorizonClient _horizon;
public IApplicationFunctions(Horizon system)
@@ -631,5 +638,31 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati
return ResultCode.Success;
}
[CommandHipc(1001)] // 10.0.0+
// PrepareForJit()
public ResultCode PrepareForJit(ServiceCtx context)
{
if (Interlocked.Exchange(ref _jitLoaded, 1) == 0)
{
string jitPath = context.Device.System.ContentManager.GetInstalledContentPath(0x010000000000003B, StorageId.BuiltInSystem, NcaContentType.Program);
string filePath = context.Device.FileSystem.SwitchPathToSystemPath(jitPath);
if (string.IsNullOrWhiteSpace(filePath))
{
throw new InvalidSystemResourceException($"JIT (010000000000003B) system title not found! The JIT will not work, provide the system archive to fix this error. (See https://github.com/Ryujinx/Ryujinx#requirements for more information)");
}
context.Device.Application.LoadServiceNca(filePath);
// FIXME: Most likely not how this should be done?
while (!context.Device.System.SmRegistry.IsServiceRegistered("jit:u"))
{
context.Device.System.SmRegistry.WaitForServiceRegistration();
}
}
return ResultCode.Success;
}
}
}

View File

@@ -30,6 +30,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro
private List<NroInfo> _nroInfos;
private KProcess _owner;
private IVirtualMemoryManager _ownerMm;
private static Random _random = new Random();
@@ -38,6 +39,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro
_nrrInfos = new List<NrrInfo>(MaxNrr);
_nroInfos = new List<NroInfo>(MaxNro);
_owner = null;
_ownerMm = null;
}
private ResultCode ParseNrr(out NrrInfo nrrInfo, ServiceCtx context, ulong nrrAddress, ulong nrrSize)
@@ -564,10 +566,12 @@ namespace Ryujinx.HLE.HOS.Services.Ro
return ResultCode.InvalidSession;
}
_owner = context.Process.HandleTable.GetKProcess(context.Request.HandleDesc.ToCopy[0]);
context.Device.System.KernelContext.Syscall.CloseHandle(context.Request.HandleDesc.ToCopy[0]);
int processHandle = context.Request.HandleDesc.ToCopy[0];
_owner = context.Process.HandleTable.GetKProcess(processHandle);
_ownerMm = _owner?.CpuMemory;
context.Device.System.KernelContext.Syscall.CloseHandle(processHandle);
if (_owner?.CpuMemory is IRefCounted rc)
if (_ownerMm is IRefCounted rc)
{
rc.IncrementReferenceCount();
}
@@ -575,6 +579,15 @@ namespace Ryujinx.HLE.HOS.Services.Ro
return ResultCode.Success;
}
[CommandHipc(10)]
// LoadNrr2(u64, u64, u64, pid)
public ResultCode LoadNrr2(ServiceCtx context)
{
context.Device.System.KernelContext.Syscall.CloseHandle(context.Request.HandleDesc.ToCopy[0]);
return LoadNrr(context);
}
protected override void Dispose(bool isDisposing)
{
if (isDisposing)
@@ -586,7 +599,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro
_nroInfos.Clear();
if (_owner?.CpuMemory is IRefCounted rc)
if (_ownerMm is IRefCounted rc)
{
rc.DecrementReferenceCount();
}

View File

@@ -4,7 +4,6 @@ using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.HLE.HOS.Kernel.Ipc;
using Ryujinx.HLE.HOS.Kernel.Process;
using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.HLE.HOS.Services.Sm;
using System;
using System.Buffers.Binary;
using System.Collections.Generic;

View File

@@ -222,7 +222,7 @@ namespace Ryujinx.HLE.HOS.Services.Settings
return ResultCode.Success;
}
[CommandHipc(60)]
[CommandHipc(60)]
// IsUserSystemClockAutomaticCorrectionEnabled() -> bool
public ResultCode IsUserSystemClockAutomaticCorrectionEnabled(ServiceCtx context)
{
@@ -234,6 +234,17 @@ namespace Ryujinx.HLE.HOS.Services.Settings
return ResultCode.Success;
}
[CommandHipc(62)]
// GetDebugModeFlag() -> bool
public ResultCode GetDebugModeFlag(ServiceCtx context)
{
context.ResponseData.Write(false);
Logger.Stub?.PrintStub(LogClass.ServiceSet);
return ResultCode.Success;
}
[CommandHipc(77)]
// GetDeviceNickName() -> buffer<nn::settings::system::DeviceNickName, 0x16>
public ResultCode GetDeviceNickName(ServiceCtx context)

View File

@@ -1,11 +1,9 @@
using Ryujinx.Common.Logging;
using Ryujinx.HLE.Exceptions;
using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.HOS.Kernel;
using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.HLE.HOS.Kernel.Ipc;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -17,21 +15,19 @@ namespace Ryujinx.HLE.HOS.Services.Sm
{
private static Dictionary<string, Type> _services;
private static readonly ConcurrentDictionary<string, KPort> _registeredServices;
private readonly SmRegistry _registry;
private readonly ServerBase _commonServer;
private bool _isInitialized;
public IUserInterface(KernelContext context)
public IUserInterface(KernelContext context, SmRegistry registry)
{
_commonServer = new ServerBase(context, "CommonServer");
_registry = registry;
}
static IUserInterface()
{
_registeredServices = new ConcurrentDictionary<string, KPort>();
_services = Assembly.GetExecutingAssembly().GetTypes()
.SelectMany(type => type.GetCustomAttributes(typeof(ServiceAttribute), true)
.Select(service => (((ServiceAttribute)service).Name, type)))
@@ -74,7 +70,7 @@ namespace Ryujinx.HLE.HOS.Services.Sm
KSession session = new KSession(context.Device.System.KernelContext);
if (_registeredServices.TryGetValue(name, out KPort port))
if (_registry.TryGetService(name, out KPort port))
{
KernelResult result = port.EnqueueIncomingSession(session.ServerSession);
@@ -82,6 +78,15 @@ namespace Ryujinx.HLE.HOS.Services.Sm
{
throw new InvalidOperationException($"Session enqueue on port returned error \"{result}\".");
}
if (context.Process.HandleTable.GenerateHandle(session.ClientSession, out int handle) != KernelResult.Success)
{
throw new InvalidOperationException("Out of handles!");
}
session.ClientSession.DecrementReferenceCount();
context.Response.HandleDesc = IpcHandleDesc.MakeMove(handle);
}
else
{
@@ -107,18 +112,18 @@ namespace Ryujinx.HLE.HOS.Services.Sm
throw new NotImplementedException(name);
}
}
if (context.Process.HandleTable.GenerateHandle(session.ClientSession, out int handle) != KernelResult.Success)
{
throw new InvalidOperationException("Out of handles!");
}
session.ServerSession.DecrementReferenceCount();
session.ClientSession.DecrementReferenceCount();
context.Response.HandleDesc = IpcHandleDesc.MakeMove(handle);
}
if (context.Process.HandleTable.GenerateHandle(session.ClientSession, out int handle) != KernelResult.Success)
{
throw new InvalidOperationException("Out of handles!");
}
session.ServerSession.DecrementReferenceCount();
session.ClientSession.DecrementReferenceCount();
context.Response.HandleDesc = IpcHandleDesc.MakeMove(handle);
return ResultCode.Success;
}
@@ -179,7 +184,7 @@ namespace Ryujinx.HLE.HOS.Services.Sm
KPort port = new KPort(context.Device.System.KernelContext, maxSessions, isLight, 0);
if (!_registeredServices.TryAdd(name, port))
if (!_registry.TryRegister(name, port))
{
return ResultCode.AlreadyRegistered;
}
@@ -219,7 +224,7 @@ namespace Ryujinx.HLE.HOS.Services.Sm
return ResultCode.InvalidName;
}
if (!_registeredServices.TryRemove(name, out _))
if (!_registry.Unregister(name))
{
return ResultCode.NotRegistered;
}

View File

@@ -0,0 +1,49 @@
using Ryujinx.HLE.HOS.Kernel.Ipc;
using System.Collections.Concurrent;
using System.Threading;
namespace Ryujinx.HLE.HOS.Services.Sm
{
class SmRegistry
{
private readonly ConcurrentDictionary<string, KPort> _registeredServices;
private readonly AutoResetEvent _serviceRegistrationEvent;
public SmRegistry()
{
_registeredServices = new ConcurrentDictionary<string, KPort>();
_serviceRegistrationEvent = new AutoResetEvent(false);
}
public bool TryGetService(string name, out KPort port)
{
return _registeredServices.TryGetValue(name, out port);
}
public bool TryRegister(string name, KPort port)
{
if (_registeredServices.TryAdd(name, port))
{
_serviceRegistrationEvent.Set();
return true;
}
return false;
}
public bool Unregister(string name)
{
return _registeredServices.TryRemove(name, out _);
}
public bool IsServiceRegistered(string name)
{
return _registeredServices.TryGetValue(name, out _);
}
public void WaitForServiceRegistration()
{
_serviceRegistrationEvent.WaitOne();
}
}
}

View File

@@ -48,7 +48,7 @@ namespace Ryujinx.Memory
{
_viewCompatible = flags.HasFlag(MemoryAllocationFlags.ViewCompatible);
_forceWindows4KBView = flags.HasFlag(MemoryAllocationFlags.ForceWindows4KBViewMapping);
_pointer = MemoryManagement.Reserve(size, _viewCompatible);
_pointer = MemoryManagement.Reserve(size, _viewCompatible, _forceWindows4KBView);
}
else
{
@@ -404,7 +404,7 @@ namespace Ryujinx.Memory
}
else
{
MemoryManagement.Free(ptr);
MemoryManagement.Free(ptr, Size, _forceWindows4KBView);
}
foreach (MemoryBlock viewStorage in _viewStorages.Keys)

View File

@@ -8,9 +8,7 @@ namespace Ryujinx.Memory
{
if (OperatingSystem.IsWindows())
{
IntPtr sizeNint = new IntPtr((long)size);
return MemoryManagementWindows.Allocate(sizeNint);
return MemoryManagementWindows.Allocate((IntPtr)size);
}
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
{
@@ -22,13 +20,11 @@ namespace Ryujinx.Memory
}
}
public static IntPtr Reserve(ulong size, bool viewCompatible)
public static IntPtr Reserve(ulong size, bool viewCompatible, bool force4KBMap)
{
if (OperatingSystem.IsWindows())
{
IntPtr sizeNint = new IntPtr((long)size);
return MemoryManagementWindows.Reserve(sizeNint, viewCompatible);
return MemoryManagementWindows.Reserve((IntPtr)size, viewCompatible, force4KBMap);
}
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
{
@@ -44,9 +40,7 @@ namespace Ryujinx.Memory
{
if (OperatingSystem.IsWindows())
{
IntPtr sizeNint = new IntPtr((long)size);
return MemoryManagementWindows.Commit(address, sizeNint);
return MemoryManagementWindows.Commit(address, (IntPtr)size);
}
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
{
@@ -62,9 +56,7 @@ namespace Ryujinx.Memory
{
if (OperatingSystem.IsWindows())
{
IntPtr sizeNint = new IntPtr((long)size);
return MemoryManagementWindows.Decommit(address, sizeNint);
return MemoryManagementWindows.Decommit(address, (IntPtr)size);
}
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
{
@@ -80,15 +72,13 @@ namespace Ryujinx.Memory
{
if (OperatingSystem.IsWindows())
{
IntPtr sizeNint = new IntPtr((long)size);
if (force4KBMap)
{
MemoryManagementWindows.MapView4KB(sharedMemory, srcOffset, address, sizeNint);
MemoryManagementWindows.MapView4KB(sharedMemory, srcOffset, address, (IntPtr)size);
}
else
{
MemoryManagementWindows.MapView(sharedMemory, srcOffset, address, sizeNint);
MemoryManagementWindows.MapView(sharedMemory, srcOffset, address, (IntPtr)size);
}
}
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
@@ -105,15 +95,13 @@ namespace Ryujinx.Memory
{
if (OperatingSystem.IsWindows())
{
IntPtr sizeNint = new IntPtr((long)size);
if (force4KBMap)
{
MemoryManagementWindows.UnmapView4KB(address, sizeNint);
MemoryManagementWindows.UnmapView4KB(address, (IntPtr)size);
}
else
{
MemoryManagementWindows.UnmapView(sharedMemory, address, sizeNint);
MemoryManagementWindows.UnmapView(sharedMemory, address, (IntPtr)size);
}
}
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
@@ -132,15 +120,13 @@ namespace Ryujinx.Memory
if (OperatingSystem.IsWindows())
{
IntPtr sizeNint = new IntPtr((long)size);
if (forView && force4KBMap)
{
result = MemoryManagementWindows.Reprotect4KB(address, sizeNint, permission, forView);
result = MemoryManagementWindows.Reprotect4KB(address, (IntPtr)size, permission, forView);
}
else
{
result = MemoryManagementWindows.Reprotect(address, sizeNint, permission, forView);
result = MemoryManagementWindows.Reprotect(address, (IntPtr)size, permission, forView);
}
}
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
@@ -158,11 +144,11 @@ namespace Ryujinx.Memory
}
}
public static bool Free(IntPtr address)
public static bool Free(IntPtr address, ulong size, bool force4KBMap)
{
if (OperatingSystem.IsWindows())
{
return MemoryManagementWindows.Free(address);
return MemoryManagementWindows.Free(address, (IntPtr)size, force4KBMap);
}
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
{
@@ -178,9 +164,7 @@ namespace Ryujinx.Memory
{
if (OperatingSystem.IsWindows())
{
IntPtr sizeNint = new IntPtr((long)size);
return MemoryManagementWindows.CreateSharedMemory(sizeNint, reserve);
return MemoryManagementWindows.CreateSharedMemory((IntPtr)size, reserve);
}
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
{

View File

@@ -7,21 +7,27 @@ namespace Ryujinx.Memory
[SupportedOSPlatform("windows")]
static class MemoryManagementWindows
{
private const int PageSize = 0x1000;
public const int PageSize = 0x1000;
private static readonly PlaceholderManager _placeholders = new PlaceholderManager();
private static readonly PlaceholderManager4KB _placeholders4KB = new PlaceholderManager4KB();
public static IntPtr Allocate(IntPtr size)
{
return AllocateInternal(size, AllocationType.Reserve | AllocationType.Commit);
}
public static IntPtr Reserve(IntPtr size, bool viewCompatible)
public static IntPtr Reserve(IntPtr size, bool viewCompatible, bool force4KBMap)
{
if (viewCompatible)
{
IntPtr baseAddress = AllocateInternal2(size, AllocationType.Reserve | AllocationType.ReservePlaceholder);
_placeholders.ReserveRange((ulong)baseAddress, (ulong)size);
if (!force4KBMap)
{
_placeholders.ReserveRange((ulong)baseAddress, (ulong)size);
}
return baseAddress;
}
@@ -69,6 +75,8 @@ namespace Ryujinx.Memory
public static void MapView4KB(IntPtr sharedMemory, ulong srcOffset, IntPtr location, IntPtr size)
{
_placeholders4KB.UnmapAndMarkRangeAsMapped(location, size);
ulong uaddress = (ulong)location;
ulong usize = (ulong)size;
IntPtr endLocation = (IntPtr)(uaddress + usize);
@@ -105,20 +113,7 @@ namespace Ryujinx.Memory
public static void UnmapView4KB(IntPtr location, IntPtr size)
{
ulong uaddress = (ulong)location;
ulong usize = (ulong)size;
IntPtr endLocation = (IntPtr)(uaddress + usize);
while (location != endLocation)
{
bool result = WindowsApi.UnmapViewOfFile2(WindowsApi.CurrentProcessHandle, location, 2);
if (!result)
{
throw new WindowsApiException("UnmapViewOfFile2");
}
location += PageSize;
}
_placeholders4KB.UnmapView(location, size);
}
public static bool Reprotect(IntPtr address, IntPtr size, MemoryPermission permission, bool forView)
@@ -151,8 +146,17 @@ namespace Ryujinx.Memory
return true;
}
public static bool Free(IntPtr address)
public static bool Free(IntPtr address, IntPtr size, bool force4KBMap)
{
if (force4KBMap)
{
_placeholders4KB.UnmapRange(address, size);
}
else
{
_placeholders.UnmapView(IntPtr.Zero, address, size);
}
return WindowsApi.VirtualFree(address, IntPtr.Zero, AllocationType.Release);
}

View File

@@ -1,5 +1,6 @@
using System;
using System.Diagnostics;
using System.Runtime.Versioning;
using System.Threading;
namespace Ryujinx.Memory.WindowsShared
@@ -7,6 +8,7 @@ namespace Ryujinx.Memory.WindowsShared
/// <summary>
/// Windows memory placeholder manager.
/// </summary>
[SupportedOSPlatform("windows")]
class PlaceholderManager
{
private const ulong MinimumPageSize = 0x1000;
@@ -203,7 +205,7 @@ namespace Ryujinx.Memory.WindowsShared
ulong endAddress = startAddress + unmapSize;
var overlaps = Array.Empty<IntervalTreeNode<ulong, ulong>>();
int count = 0;
int count;
lock (_mappings)
{
@@ -226,8 +228,11 @@ namespace Ryujinx.Memory.WindowsShared
ulong overlapEnd = overlap.End;
ulong overlapValue = overlap.Value;
_mappings.Remove(overlap);
_mappings.Add(overlapStart, overlapEnd, ulong.MaxValue);
lock (_mappings)
{
_mappings.Remove(overlap);
_mappings.Add(overlapStart, overlapEnd, ulong.MaxValue);
}
bool overlapStartsBefore = overlapStart < startAddress;
bool overlapEndsAfter = overlapEnd > endAddress;
@@ -364,7 +369,7 @@ namespace Ryujinx.Memory.WindowsShared
ulong endAddress = reprotectAddress + reprotectSize;
var overlaps = Array.Empty<IntervalTreeNode<ulong, ulong>>();
int count = 0;
int count;
lock (_mappings)
{
@@ -534,7 +539,7 @@ namespace Ryujinx.Memory.WindowsShared
{
ulong endAddress = address + size;
var overlaps = Array.Empty<IntervalTreeNode<ulong, MemoryPermission>>();
int count = 0;
int count;
lock (_protections)
{

View File

@@ -0,0 +1,170 @@
using System;
using System.Runtime.Versioning;
namespace Ryujinx.Memory.WindowsShared
{
/// <summary>
/// Windows 4KB memory placeholder manager.
/// </summary>
[SupportedOSPlatform("windows")]
class PlaceholderManager4KB
{
private const int PageSize = MemoryManagementWindows.PageSize;
private readonly IntervalTree<ulong, byte> _mappings;
/// <summary>
/// Creates a new instance of the Windows 4KB memory placeholder manager.
/// </summary>
public PlaceholderManager4KB()
{
_mappings = new IntervalTree<ulong, byte>();
}
/// <summary>
/// Unmaps the specified range of memory and marks it as mapped internally.
/// </summary>
/// <remarks>
/// Since this marks the range as mapped, the expectation is that the range will be mapped after calling this method.
/// </remarks>
/// <param name="location">Memory address to unmap and mark as mapped</param>
/// <param name="size">Size of the range in bytes</param>
public void UnmapAndMarkRangeAsMapped(IntPtr location, IntPtr size)
{
ulong startAddress = (ulong)location;
ulong unmapSize = (ulong)size;
ulong endAddress = startAddress + unmapSize;
var overlaps = Array.Empty<IntervalTreeNode<ulong, byte>>();
int count = 0;
lock (_mappings)
{
count = _mappings.Get(startAddress, endAddress, ref overlaps);
}
for (int index = 0; index < count; index++)
{
var overlap = overlaps[index];
// Tree operations might modify the node start/end values, so save a copy before we modify the tree.
ulong overlapStart = overlap.Start;
ulong overlapEnd = overlap.End;
ulong overlapValue = overlap.Value;
_mappings.Remove(overlap);
ulong unmapStart = Math.Max(overlapStart, startAddress);
ulong unmapEnd = Math.Min(overlapEnd, endAddress);
if (overlapStart < startAddress)
{
startAddress = overlapStart;
}
if (overlapEnd > endAddress)
{
endAddress = overlapEnd;
}
ulong currentAddress = unmapStart;
while (currentAddress < unmapEnd)
{
WindowsApi.UnmapViewOfFile2(WindowsApi.CurrentProcessHandle, (IntPtr)currentAddress, 2);
currentAddress += PageSize;
}
}
_mappings.Add(startAddress, endAddress, 0);
}
/// <summary>
/// Unmaps views at the specified memory range.
/// </summary>
/// <param name="location">Address of the range</param>
/// <param name="size">Size of the range in bytes</param>
public void UnmapView(IntPtr location, IntPtr size)
{
ulong startAddress = (ulong)location;
ulong unmapSize = (ulong)size;
ulong endAddress = startAddress + unmapSize;
var overlaps = Array.Empty<IntervalTreeNode<ulong, byte>>();
int count = 0;
lock (_mappings)
{
count = _mappings.Get(startAddress, endAddress, ref overlaps);
}
for (int index = 0; index < count; index++)
{
var overlap = overlaps[index];
// Tree operations might modify the node start/end values, so save a copy before we modify the tree.
ulong overlapStart = overlap.Start;
ulong overlapEnd = overlap.End;
_mappings.Remove(overlap);
if (overlapStart < startAddress)
{
_mappings.Add(overlapStart, startAddress, 0);
}
if (overlapEnd > endAddress)
{
_mappings.Add(endAddress, overlapEnd, 0);
}
ulong unmapStart = Math.Max(overlapStart, startAddress);
ulong unmapEnd = Math.Min(overlapEnd, endAddress);
ulong currentAddress = unmapStart;
while (currentAddress < unmapEnd)
{
WindowsApi.UnmapViewOfFile2(WindowsApi.CurrentProcessHandle, (IntPtr)currentAddress, 2);
currentAddress += PageSize;
}
}
}
/// <summary>
/// Unmaps mapped memory at a given range.
/// </summary>
/// <param name="location">Address of the range</param>
/// <param name="size">Size of the range in bytes</param>
public void UnmapRange(IntPtr location, IntPtr size)
{
ulong startAddress = (ulong)location;
ulong unmapSize = (ulong)size;
ulong endAddress = startAddress + unmapSize;
var overlaps = Array.Empty<IntervalTreeNode<ulong, byte>>();
int count = 0;
lock (_mappings)
{
count = _mappings.Get(startAddress, endAddress, ref overlaps);
}
for (int index = 0; index < count; index++)
{
var overlap = overlaps[index];
// Tree operations might modify the node start/end values, so save a copy before we modify the tree.
ulong unmapStart = Math.Max(overlap.Start, startAddress);
ulong unmapEnd = Math.Min(overlap.End, endAddress);
_mappings.Remove(overlap);
ulong currentAddress = unmapStart;
while (currentAddress < unmapEnd)
{
WindowsApi.UnmapViewOfFile2(WindowsApi.CurrentProcessHandle, (IntPtr)currentAddress, 2);
currentAddress += PageSize;
}
}
}
}
}