Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4b947713a6 | |||
| efa0cc7554 | |||
| 1c0813d09d | |||
| a873faf0e7 | |||
| 834531483f | |||
| 09b225e928 | |||
| 9886d49204 | |||
| 4e9c4587e5 | |||
| f441d3e01d | |||
| cf1ad265f9 | |||
| 6773343d46 | |||
| 45af4d2517 | |||
| c279c7d5de | |||
| 753ca01c0d | |||
| 5aedeebfe9 | |||
| 5c67efd291 | |||
| b2354768c4 | |||
| 745bd91250 | |||
| 07ab817557 | |||
| 0e8a41b198 | |||
| 969e94f913 | |||
| 2e2d26d49d |
@@ -7,6 +7,7 @@ namespace ARMeilleure.Memory
|
|||||||
public const int DefaultGranularity = 65536; // Mapping granularity in Windows.
|
public const int DefaultGranularity = 65536; // Mapping granularity in Windows.
|
||||||
|
|
||||||
public IJitMemoryBlock Block { get; }
|
public IJitMemoryBlock Block { get; }
|
||||||
|
public IJitMemoryAllocator Allocator { get; }
|
||||||
|
|
||||||
public nint Pointer => Block.Pointer;
|
public nint Pointer => Block.Pointer;
|
||||||
|
|
||||||
@@ -21,6 +22,7 @@ namespace ARMeilleure.Memory
|
|||||||
granularity = DefaultGranularity;
|
granularity = DefaultGranularity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Allocator = allocator;
|
||||||
Block = allocator.Reserve(maxSize);
|
Block = allocator.Reserve(maxSize);
|
||||||
_maxSize = maxSize;
|
_maxSize = maxSize;
|
||||||
_sizeGranularity = granularity;
|
_sizeGranularity = granularity;
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ using ARMeilleure.CodeGen;
|
|||||||
using ARMeilleure.CodeGen.Unwinding;
|
using ARMeilleure.CodeGen.Unwinding;
|
||||||
using ARMeilleure.Memory;
|
using ARMeilleure.Memory;
|
||||||
using ARMeilleure.Native;
|
using ARMeilleure.Native;
|
||||||
|
using Humanizer;
|
||||||
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@@ -18,9 +20,8 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
private static readonly int _pageMask = _pageSize - 1;
|
private static readonly int _pageMask = _pageSize - 1;
|
||||||
|
|
||||||
private const int CodeAlignment = 4; // Bytes.
|
private const int CodeAlignment = 4; // Bytes.
|
||||||
private const int CacheSize = 2047 * 1024 * 1024;
|
private const int CacheSize = 256 * 1024 * 1024;
|
||||||
|
|
||||||
private static ReservedRegion _jitRegion;
|
|
||||||
private static JitCacheInvalidation _jitCacheInvalidator;
|
private static JitCacheInvalidation _jitCacheInvalidator;
|
||||||
|
|
||||||
private static CacheMemoryAllocator _cacheAllocator;
|
private static CacheMemoryAllocator _cacheAllocator;
|
||||||
@@ -30,6 +31,9 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
private static readonly Lock _lock = new();
|
private static readonly Lock _lock = new();
|
||||||
private static bool _initialized;
|
private static bool _initialized;
|
||||||
|
|
||||||
|
private static readonly List<ReservedRegion> _jitRegions = new();
|
||||||
|
private static int _activeRegionIndex = 0;
|
||||||
|
|
||||||
[SupportedOSPlatform("windows")]
|
[SupportedOSPlatform("windows")]
|
||||||
[LibraryImport("kernel32.dll", SetLastError = true)]
|
[LibraryImport("kernel32.dll", SetLastError = true)]
|
||||||
public static partial nint FlushInstructionCache(nint hProcess, nint lpAddress, nuint dwSize);
|
public static partial nint FlushInstructionCache(nint hProcess, nint lpAddress, nuint dwSize);
|
||||||
@@ -48,7 +52,9 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_jitRegion = new ReservedRegion(allocator, CacheSize);
|
var firstRegion = new ReservedRegion(allocator, CacheSize);
|
||||||
|
_jitRegions.Add(firstRegion);
|
||||||
|
_activeRegionIndex = 0;
|
||||||
|
|
||||||
if (!OperatingSystem.IsWindows() && !OperatingSystem.IsMacOS())
|
if (!OperatingSystem.IsWindows() && !OperatingSystem.IsMacOS())
|
||||||
{
|
{
|
||||||
@@ -59,7 +65,9 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
|
|
||||||
if (OperatingSystem.IsWindows())
|
if (OperatingSystem.IsWindows())
|
||||||
{
|
{
|
||||||
JitUnwindWindows.InstallFunctionTableHandler(_jitRegion.Pointer, CacheSize, _jitRegion.Pointer + Allocate(_pageSize));
|
JitUnwindWindows.InstallFunctionTableHandler(
|
||||||
|
firstRegion.Pointer, CacheSize, firstRegion.Pointer + Allocate(_pageSize)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
_initialized = true;
|
_initialized = true;
|
||||||
@@ -75,8 +83,8 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
Debug.Assert(_initialized);
|
Debug.Assert(_initialized);
|
||||||
|
|
||||||
int funcOffset = Allocate(code.Length);
|
int funcOffset = Allocate(code.Length);
|
||||||
|
ReservedRegion targetRegion = _jitRegions[_activeRegionIndex];
|
||||||
nint funcPtr = _jitRegion.Pointer + funcOffset;
|
nint funcPtr = targetRegion.Pointer + funcOffset;
|
||||||
|
|
||||||
if (OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
|
if (OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
|
||||||
{
|
{
|
||||||
@@ -90,9 +98,9 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ReprotectAsWritable(funcOffset, code.Length);
|
ReprotectAsWritable(targetRegion, funcOffset, code.Length);
|
||||||
Marshal.Copy(code, 0, funcPtr, code.Length);
|
Marshal.Copy(code, 0, funcPtr, code.Length);
|
||||||
ReprotectAsExecutable(funcOffset, code.Length);
|
ReprotectAsExecutable(targetRegion, funcOffset, code.Length);
|
||||||
|
|
||||||
if (OperatingSystem.IsWindows() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
|
if (OperatingSystem.IsWindows() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
|
||||||
{
|
{
|
||||||
@@ -116,52 +124,83 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
{
|
{
|
||||||
Debug.Assert(_initialized);
|
Debug.Assert(_initialized);
|
||||||
|
|
||||||
int funcOffset = (int)(pointer.ToInt64() - _jitRegion.Pointer.ToInt64());
|
foreach (var region in _jitRegions)
|
||||||
|
|
||||||
if (TryFind(funcOffset, out CacheEntry entry, out int entryIndex) && entry.Offset == funcOffset)
|
|
||||||
{
|
{
|
||||||
_cacheAllocator.Free(funcOffset, AlignCodeSize(entry.Size));
|
if (pointer.ToInt64() < region.Pointer.ToInt64() ||
|
||||||
_cacheEntries.RemoveAt(entryIndex);
|
pointer.ToInt64() >= (region.Pointer + CacheSize).ToInt64())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int funcOffset = (int)(pointer.ToInt64() - region.Pointer.ToInt64());
|
||||||
|
|
||||||
|
if (TryFind(funcOffset, out CacheEntry entry, out int entryIndex) && entry.Offset == funcOffset)
|
||||||
|
{
|
||||||
|
_cacheAllocator.Free(funcOffset, AlignCodeSize(entry.Size));
|
||||||
|
_cacheEntries.RemoveAt(entryIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ReprotectAsWritable(int offset, int size)
|
private static void ReprotectAsWritable(ReservedRegion region, int offset, int size)
|
||||||
{
|
{
|
||||||
int endOffs = offset + size;
|
int endOffs = offset + size;
|
||||||
|
|
||||||
int regionStart = offset & ~_pageMask;
|
int regionStart = offset & ~_pageMask;
|
||||||
int regionEnd = (endOffs + _pageMask) & ~_pageMask;
|
int regionEnd = (endOffs + _pageMask) & ~_pageMask;
|
||||||
|
|
||||||
_jitRegion.Block.MapAsRwx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
region.Block.MapAsRwx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ReprotectAsExecutable(int offset, int size)
|
private static void ReprotectAsExecutable(ReservedRegion region, int offset, int size)
|
||||||
{
|
{
|
||||||
int endOffs = offset + size;
|
int endOffs = offset + size;
|
||||||
|
|
||||||
int regionStart = offset & ~_pageMask;
|
int regionStart = offset & ~_pageMask;
|
||||||
int regionEnd = (endOffs + _pageMask) & ~_pageMask;
|
int regionEnd = (endOffs + _pageMask) & ~_pageMask;
|
||||||
|
|
||||||
_jitRegion.Block.MapAsRx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
region.Block.MapAsRx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int Allocate(int codeSize)
|
private static int Allocate(int codeSize)
|
||||||
{
|
{
|
||||||
codeSize = AlignCodeSize(codeSize);
|
codeSize = AlignCodeSize(codeSize);
|
||||||
|
|
||||||
int allocOffset = _cacheAllocator.Allocate(codeSize);
|
for (int i = _activeRegionIndex; i < _jitRegions.Count; i++)
|
||||||
|
|
||||||
if (allocOffset < 0)
|
|
||||||
{
|
{
|
||||||
throw new OutOfMemoryException("JIT Cache exhausted.");
|
int allocOffset = _cacheAllocator.Allocate(codeSize);
|
||||||
|
|
||||||
|
if (allocOffset >= 0)
|
||||||
|
{
|
||||||
|
_jitRegions[i].ExpandIfNeeded((ulong)allocOffset + (ulong)codeSize);
|
||||||
|
_activeRegionIndex = i;
|
||||||
|
return allocOffset;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_jitRegion.ExpandIfNeeded((ulong)allocOffset + (ulong)codeSize);
|
int exhaustedRegion = _activeRegionIndex;
|
||||||
|
var newRegion = new ReservedRegion(_jitRegions[0].Allocator, CacheSize);
|
||||||
|
_jitRegions.Add(newRegion);
|
||||||
|
_activeRegionIndex = _jitRegions.Count - 1;
|
||||||
|
|
||||||
|
int newRegionNumber = _activeRegionIndex;
|
||||||
|
|
||||||
return allocOffset;
|
Logger.Warning?.Print(LogClass.Cpu, $"JIT Cache Region {exhaustedRegion} exhausted, creating new Cache Region {newRegionNumber} ({((newRegionNumber + 1) * CacheSize).Bytes()} Total Allocation).");
|
||||||
|
|
||||||
|
_cacheAllocator = new CacheMemoryAllocator(CacheSize);
|
||||||
|
|
||||||
|
int allocOffsetNew = _cacheAllocator.Allocate(codeSize);
|
||||||
|
if (allocOffsetNew < 0)
|
||||||
|
{
|
||||||
|
throw new OutOfMemoryException("Failed to allocate in new Cache Region!");
|
||||||
|
}
|
||||||
|
|
||||||
|
newRegion.ExpandIfNeeded((ulong)allocOffsetNew + (ulong)codeSize);
|
||||||
|
return allocOffsetNew;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static int AlignCodeSize(int codeSize)
|
private static int AlignCodeSize(int codeSize)
|
||||||
{
|
{
|
||||||
return checked(codeSize + (CodeAlignment - 1)) & ~(CodeAlignment - 1);
|
return checked(codeSize + (CodeAlignment - 1)) & ~(CodeAlignment - 1);
|
||||||
@@ -185,18 +224,21 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
{
|
{
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
int index = _cacheEntries.BinarySearch(new CacheEntry(offset, 0, default));
|
foreach (var region in _jitRegions)
|
||||||
|
|
||||||
if (index < 0)
|
|
||||||
{
|
{
|
||||||
index = ~index - 1;
|
int index = _cacheEntries.BinarySearch(new CacheEntry(offset, 0, default));
|
||||||
}
|
|
||||||
|
|
||||||
if (index >= 0)
|
if (index < 0)
|
||||||
{
|
{
|
||||||
entry = _cacheEntries[index];
|
index = ~index - 1;
|
||||||
entryIndex = index;
|
}
|
||||||
return true;
|
|
||||||
|
if (index >= 0)
|
||||||
|
{
|
||||||
|
entry = _cacheEntries[index];
|
||||||
|
entryIndex = index;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
using ARMeilleure.Memory;
|
using ARMeilleure.Memory;
|
||||||
|
using Humanizer;
|
||||||
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@@ -15,9 +17,8 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||||||
private static readonly int _pageMask = _pageSize - 1;
|
private static readonly int _pageMask = _pageSize - 1;
|
||||||
|
|
||||||
private const int CodeAlignment = 4; // Bytes.
|
private const int CodeAlignment = 4; // Bytes.
|
||||||
private const int CacheSize = 2047 * 1024 * 1024;
|
private const int CacheSize = 256 * 1024 * 1024;
|
||||||
|
|
||||||
private static ReservedRegion _jitRegion;
|
|
||||||
private static JitCacheInvalidation _jitCacheInvalidator;
|
private static JitCacheInvalidation _jitCacheInvalidator;
|
||||||
|
|
||||||
private static CacheMemoryAllocator _cacheAllocator;
|
private static CacheMemoryAllocator _cacheAllocator;
|
||||||
@@ -26,6 +27,8 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||||||
|
|
||||||
private static readonly Lock _lock = new();
|
private static readonly Lock _lock = new();
|
||||||
private static bool _initialized;
|
private static bool _initialized;
|
||||||
|
private static readonly List<ReservedRegion> _jitRegions = new();
|
||||||
|
private static int _activeRegionIndex = 0;
|
||||||
|
|
||||||
[SupportedOSPlatform("windows")]
|
[SupportedOSPlatform("windows")]
|
||||||
[LibraryImport("kernel32.dll", SetLastError = true)]
|
[LibraryImport("kernel32.dll", SetLastError = true)]
|
||||||
@@ -45,7 +48,9 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_jitRegion = new ReservedRegion(allocator, CacheSize);
|
var firstRegion = new ReservedRegion(allocator, CacheSize);
|
||||||
|
_jitRegions.Add(firstRegion);
|
||||||
|
_activeRegionIndex = 0;
|
||||||
|
|
||||||
if (!OperatingSystem.IsWindows() && !OperatingSystem.IsMacOS())
|
if (!OperatingSystem.IsWindows() && !OperatingSystem.IsMacOS())
|
||||||
{
|
{
|
||||||
@@ -65,8 +70,8 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||||||
Debug.Assert(_initialized);
|
Debug.Assert(_initialized);
|
||||||
|
|
||||||
int funcOffset = Allocate(code.Length);
|
int funcOffset = Allocate(code.Length);
|
||||||
|
ReservedRegion targetRegion = _jitRegions[_activeRegionIndex];
|
||||||
nint funcPtr = _jitRegion.Pointer + funcOffset;
|
nint funcPtr = targetRegion.Pointer + funcOffset;
|
||||||
|
|
||||||
if (OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
|
if (OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
|
||||||
{
|
{
|
||||||
@@ -80,18 +85,11 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ReprotectAsWritable(funcOffset, code.Length);
|
ReprotectAsWritable(targetRegion, funcOffset, code.Length);
|
||||||
code.CopyTo(new Span<byte>((void*)funcPtr, code.Length));
|
Marshal.Copy(code.ToArray(), 0, funcPtr, code.Length);
|
||||||
ReprotectAsExecutable(funcOffset, code.Length);
|
ReprotectAsExecutable(targetRegion, funcOffset, code.Length);
|
||||||
|
|
||||||
if (OperatingSystem.IsWindows() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
|
_jitCacheInvalidator?.Invalidate(funcPtr, (ulong)code.Length);
|
||||||
{
|
|
||||||
FlushInstructionCache(Process.GetCurrentProcess().Handle, funcPtr, (nuint)code.Length);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_jitCacheInvalidator?.Invalidate(funcPtr, (ulong)code.Length);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Add(funcOffset, code.Length);
|
Add(funcOffset, code.Length);
|
||||||
@@ -106,50 +104,80 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||||||
{
|
{
|
||||||
Debug.Assert(_initialized);
|
Debug.Assert(_initialized);
|
||||||
|
|
||||||
int funcOffset = (int)(pointer.ToInt64() - _jitRegion.Pointer.ToInt64());
|
foreach (var region in _jitRegions)
|
||||||
|
|
||||||
if (TryFind(funcOffset, out CacheEntry entry, out int entryIndex) && entry.Offset == funcOffset)
|
|
||||||
{
|
{
|
||||||
_cacheAllocator.Free(funcOffset, AlignCodeSize(entry.Size));
|
if (pointer.ToInt64() < region.Pointer.ToInt64() ||
|
||||||
_cacheEntries.RemoveAt(entryIndex);
|
pointer.ToInt64() >= (region.Pointer + CacheSize).ToInt64())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int funcOffset = (int)(pointer.ToInt64() - region.Pointer.ToInt64());
|
||||||
|
|
||||||
|
if (TryFind(funcOffset, out CacheEntry entry, out int entryIndex) && entry.Offset == funcOffset)
|
||||||
|
{
|
||||||
|
_cacheAllocator.Free(funcOffset, AlignCodeSize(entry.Size));
|
||||||
|
_cacheEntries.RemoveAt(entryIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ReprotectAsWritable(int offset, int size)
|
private static void ReprotectAsWritable(ReservedRegion region, int offset, int size)
|
||||||
{
|
{
|
||||||
int endOffs = offset + size;
|
int endOffs = offset + size;
|
||||||
|
|
||||||
int regionStart = offset & ~_pageMask;
|
int regionStart = offset & ~_pageMask;
|
||||||
int regionEnd = (endOffs + _pageMask) & ~_pageMask;
|
int regionEnd = (endOffs + _pageMask) & ~_pageMask;
|
||||||
|
|
||||||
_jitRegion.Block.MapAsRwx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
region.Block.MapAsRwx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ReprotectAsExecutable(int offset, int size)
|
private static void ReprotectAsExecutable(ReservedRegion region, int offset, int size)
|
||||||
{
|
{
|
||||||
int endOffs = offset + size;
|
int endOffs = offset + size;
|
||||||
|
|
||||||
int regionStart = offset & ~_pageMask;
|
int regionStart = offset & ~_pageMask;
|
||||||
int regionEnd = (endOffs + _pageMask) & ~_pageMask;
|
int regionEnd = (endOffs + _pageMask) & ~_pageMask;
|
||||||
|
|
||||||
_jitRegion.Block.MapAsRx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
region.Block.MapAsRx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int Allocate(int codeSize)
|
private static int Allocate(int codeSize)
|
||||||
{
|
{
|
||||||
codeSize = AlignCodeSize(codeSize);
|
codeSize = AlignCodeSize(codeSize);
|
||||||
|
|
||||||
int allocOffset = _cacheAllocator.Allocate(codeSize);
|
for (int i = _activeRegionIndex; i < _jitRegions.Count; i++)
|
||||||
|
|
||||||
if (allocOffset < 0)
|
|
||||||
{
|
{
|
||||||
throw new OutOfMemoryException("JIT Cache exhausted.");
|
int allocOffset = _cacheAllocator.Allocate(codeSize);
|
||||||
|
|
||||||
|
if (allocOffset >= 0)
|
||||||
|
{
|
||||||
|
_jitRegions[i].ExpandIfNeeded((ulong)allocOffset + (ulong)codeSize);
|
||||||
|
_activeRegionIndex = i;
|
||||||
|
return allocOffset;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_jitRegion.ExpandIfNeeded((ulong)allocOffset + (ulong)codeSize);
|
int exhaustedRegion = _activeRegionIndex;
|
||||||
|
var newRegion = new ReservedRegion(_jitRegions[0].Allocator, CacheSize);
|
||||||
|
_jitRegions.Add(newRegion);
|
||||||
|
_activeRegionIndex = _jitRegions.Count - 1;
|
||||||
|
|
||||||
|
int newRegionNumber = _activeRegionIndex;
|
||||||
|
|
||||||
return allocOffset;
|
Logger.Warning?.Print(LogClass.Cpu, $"JIT Cache Region {exhaustedRegion} exhausted, creating new Cache Region {newRegionNumber} ({((newRegionNumber + 1) * CacheSize).Bytes()} Total Allocation).");
|
||||||
|
|
||||||
|
_cacheAllocator = new CacheMemoryAllocator(CacheSize);
|
||||||
|
|
||||||
|
int allocOffsetNew = _cacheAllocator.Allocate(codeSize);
|
||||||
|
if (allocOffsetNew < 0)
|
||||||
|
{
|
||||||
|
throw new OutOfMemoryException("Failed to allocate in new Cache Region!");
|
||||||
|
}
|
||||||
|
|
||||||
|
newRegion.ExpandIfNeeded((ulong)allocOffsetNew + (ulong)codeSize);
|
||||||
|
return allocOffsetNew;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int AlignCodeSize(int codeSize)
|
private static int AlignCodeSize(int codeSize)
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||||||
{
|
{
|
||||||
private const int CodeAlignment = 4; // Bytes.
|
private const int CodeAlignment = 4; // Bytes.
|
||||||
private const int SharedCacheSize = 2047 * 1024 * 1024;
|
private const int SharedCacheSize = 2047 * 1024 * 1024;
|
||||||
private const int LocalCacheSize = 128 * 1024 * 1024;
|
private const int LocalCacheSize = 256 * 1024 * 1024;
|
||||||
|
|
||||||
// How many calls to the same function we allow until we pad the shared cache to force the function to become available there
|
// How many calls to the same function we allow until we pad the shared cache to force the function to become available there
|
||||||
// and allow the guest to take the fast path.
|
// and allow the guest to take the fast path.
|
||||||
|
|||||||
@@ -150,6 +150,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
|
|||||||
{ BsdSocketOption.SoLinger, SocketOptionName.Linger },
|
{ BsdSocketOption.SoLinger, SocketOptionName.Linger },
|
||||||
{ BsdSocketOption.SoOobInline, SocketOptionName.OutOfBandInline },
|
{ BsdSocketOption.SoOobInline, SocketOptionName.OutOfBandInline },
|
||||||
{ BsdSocketOption.SoReusePort, SocketOptionName.ReuseAddress },
|
{ BsdSocketOption.SoReusePort, SocketOptionName.ReuseAddress },
|
||||||
|
{ BsdSocketOption.SoNoSigpipe, SocketOptionName.DontLinger },
|
||||||
{ BsdSocketOption.SoSndBuf, SocketOptionName.SendBuffer },
|
{ BsdSocketOption.SoSndBuf, SocketOptionName.SendBuffer },
|
||||||
{ BsdSocketOption.SoRcvBuf, SocketOptionName.ReceiveBuffer },
|
{ BsdSocketOption.SoRcvBuf, SocketOptionName.ReceiveBuffer },
|
||||||
{ BsdSocketOption.SoSndLoWat, SocketOptionName.SendLowWater },
|
{ BsdSocketOption.SoSndLoWat, SocketOptionName.SendLowWater },
|
||||||
|
|||||||
@@ -55,9 +55,21 @@
|
|||||||
Tag="{Binding AppData.IdString}"
|
Tag="{Binding AppData.IdString}"
|
||||||
Text="{Binding AppData.LocalizedStatus}"
|
Text="{Binding AppData.LocalizedStatus}"
|
||||||
Foreground="{Binding AppData.PlayabilityStatus, Converter={x:Static helpers:PlayabilityStatusConverter.Shared}}"
|
Foreground="{Binding AppData.PlayabilityStatus, Converter={x:Static helpers:PlayabilityStatusConverter.Shared}}"
|
||||||
ToolTip.Tip="{Binding AppData.LocalizedStatusTooltip}"
|
|
||||||
TextAlignment="Start"
|
TextAlignment="Start"
|
||||||
TextWrapping="Wrap" />
|
TextWrapping="Wrap">
|
||||||
|
<ToolTip.Tip>
|
||||||
|
<StackPanel Orientation="Vertical">
|
||||||
|
<TextBlock
|
||||||
|
Text="{Binding AppData.LocalizedStatusTooltip}" />
|
||||||
|
<Separator
|
||||||
|
Margin="0, 10, 0, 10"
|
||||||
|
IsVisible="{Binding AppData.HasCompatibilityLabels}" />
|
||||||
|
<TextBlock
|
||||||
|
IsVisible="{Binding AppData.HasCompatibilityLabels}"
|
||||||
|
Text="{Binding AppData.FormattedCompatibilityLabels}" />
|
||||||
|
</StackPanel>
|
||||||
|
</ToolTip.Tip>
|
||||||
|
</TextBlock>
|
||||||
<Button.Styles>
|
<Button.Styles>
|
||||||
<Style Selector="Button">
|
<Style Selector="Button">
|
||||||
<Setter Property="MinWidth"
|
<Setter Property="MinWidth"
|
||||||
|
|||||||
@@ -93,8 +93,19 @@
|
|||||||
IsVisible="{Binding HasPlayabilityInfo}"
|
IsVisible="{Binding HasPlayabilityInfo}"
|
||||||
Background="{DynamicResource AppListBackgroundColor}"
|
Background="{DynamicResource AppListBackgroundColor}"
|
||||||
Margin="-1, 0, 0, 0"
|
Margin="-1, 0, 0, 0"
|
||||||
Padding="0"
|
Padding="0">
|
||||||
ToolTip.Tip="{Binding LocalizedStatusTooltip}">
|
<ToolTip.Tip>
|
||||||
|
<StackPanel Orientation="Vertical">
|
||||||
|
<TextBlock
|
||||||
|
Text="{Binding LocalizedStatusTooltip}" />
|
||||||
|
<Separator
|
||||||
|
Margin="0, 10, 0, 10"
|
||||||
|
IsVisible="{Binding HasCompatibilityLabels}" />
|
||||||
|
<TextBlock
|
||||||
|
IsVisible="{Binding HasCompatibilityLabels}"
|
||||||
|
Text="{Binding FormattedCompatibilityLabels}" />
|
||||||
|
</StackPanel>
|
||||||
|
</ToolTip.Tip>
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Margin="1.5"
|
Margin="1.5"
|
||||||
Tag="{Binding IdString}"
|
Tag="{Binding IdString}"
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using Gommon;
|
||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
using LibHac.Fs.Fsa;
|
using LibHac.Fs.Fsa;
|
||||||
@@ -14,6 +15,7 @@ using Ryujinx.HLE.FileSystem;
|
|||||||
using Ryujinx.HLE.Loaders.Processes.Extensions;
|
using Ryujinx.HLE.Loaders.Processes.Extensions;
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Ryujinx.Ava.Utilities.AppLibrary
|
namespace Ryujinx.Ava.Utilities.AppLibrary
|
||||||
@@ -32,33 +34,12 @@ namespace Ryujinx.Ava.Utilities.AppLibrary
|
|||||||
set
|
set
|
||||||
{
|
{
|
||||||
_id = value;
|
_id = value;
|
||||||
PlayabilityStatus = CompatibilityCsv.GetStatus(Id);
|
|
||||||
|
Compatibility = CompatibilityCsv.Find(Id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public string Developer { get; set; } = "Unknown";
|
public string Developer { get; set; } = "Unknown";
|
||||||
public string Version { get; set; } = "0";
|
public string Version { get; set; } = "0";
|
||||||
|
|
||||||
public bool HasPlayabilityInfo => PlayabilityStatus != null;
|
|
||||||
|
|
||||||
public string LocalizedStatus =>
|
|
||||||
PlayabilityStatus.HasValue
|
|
||||||
? LocaleManager.Instance[PlayabilityStatus!.Value]
|
|
||||||
: string.Empty;
|
|
||||||
|
|
||||||
public LocaleKeys? PlayabilityStatus { get; set; }
|
|
||||||
public string LocalizedStatusTooltip =>
|
|
||||||
PlayabilityStatus.HasValue
|
|
||||||
#pragma warning disable CS8509 // It is exhaustive for any value this property can contain.
|
|
||||||
? LocaleManager.Instance[PlayabilityStatus!.Value switch
|
|
||||||
#pragma warning restore CS8509
|
|
||||||
{
|
|
||||||
LocaleKeys.CompatibilityListPlayable => LocaleKeys.CompatibilityListPlayableTooltip,
|
|
||||||
LocaleKeys.CompatibilityListIngame => LocaleKeys.CompatibilityListIngameTooltip,
|
|
||||||
LocaleKeys.CompatibilityListMenus => LocaleKeys.CompatibilityListMenusTooltip,
|
|
||||||
LocaleKeys.CompatibilityListBoots => LocaleKeys.CompatibilityListBootsTooltip,
|
|
||||||
LocaleKeys.CompatibilityListNothing => LocaleKeys.CompatibilityListNothingTooltip,
|
|
||||||
}]
|
|
||||||
: string.Empty;
|
|
||||||
public int PlayerCount { get; set; }
|
public int PlayerCount { get; set; }
|
||||||
public int GameCount { get; set; }
|
public int GameCount { get; set; }
|
||||||
|
|
||||||
@@ -83,6 +64,53 @@ namespace Ryujinx.Ava.Utilities.AppLibrary
|
|||||||
public string LastPlayedString => ValueFormatUtils.FormatDateTime(LastPlayed)?.Replace(" ", "\n");
|
public string LastPlayedString => ValueFormatUtils.FormatDateTime(LastPlayed)?.Replace(" ", "\n");
|
||||||
|
|
||||||
public string FileSizeString => ValueFormatUtils.FormatFileSize(FileSize);
|
public string FileSizeString => ValueFormatUtils.FormatFileSize(FileSize);
|
||||||
|
|
||||||
|
public Optional<CompatibilityEntry> Compatibility { get; private set; }
|
||||||
|
|
||||||
|
public bool HasPlayabilityInfo => Compatibility.HasValue;
|
||||||
|
|
||||||
|
public string LocalizedStatus => Compatibility.Convert(x => x.LocalizedStatus);
|
||||||
|
|
||||||
|
public bool HasCompatibilityLabels => !FormattedCompatibilityLabels.Equals(string.Empty);
|
||||||
|
|
||||||
|
public string FormattedCompatibilityLabels
|
||||||
|
=> Compatibility.Convert(x => x.FormattedIssueLabels).OrElse(string.Empty);
|
||||||
|
|
||||||
|
public LocaleKeys? PlayabilityStatus => Compatibility.Convert(x => x.Status).OrElse(null);
|
||||||
|
|
||||||
|
public string CompatibilityToolTip
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
StringBuilder sb = new(LocalizedStatusTooltip);
|
||||||
|
|
||||||
|
string formattedCompatibilityLabels = FormattedCompatibilityLabels;
|
||||||
|
if (!string.IsNullOrEmpty(formattedCompatibilityLabels))
|
||||||
|
{
|
||||||
|
sb.AppendLine()
|
||||||
|
.AppendLine()
|
||||||
|
.Append(formattedCompatibilityLabels);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public string LocalizedStatusTooltip =>
|
||||||
|
Compatibility.Convert(x =>
|
||||||
|
#pragma warning disable CS8509 It is exhaustive for all possible values this can contain.
|
||||||
|
LocaleManager.Instance[x.Status switch
|
||||||
|
#pragma warning restore CS8509
|
||||||
|
{
|
||||||
|
LocaleKeys.CompatibilityListPlayable => LocaleKeys.CompatibilityListPlayableTooltip,
|
||||||
|
LocaleKeys.CompatibilityListIngame => LocaleKeys.CompatibilityListIngameTooltip,
|
||||||
|
LocaleKeys.CompatibilityListMenus => LocaleKeys.CompatibilityListMenusTooltip,
|
||||||
|
LocaleKeys.CompatibilityListBoots => LocaleKeys.CompatibilityListBootsTooltip,
|
||||||
|
LocaleKeys.CompatibilityListNothing => LocaleKeys.CompatibilityListNothingTooltip,
|
||||||
|
}]
|
||||||
|
).OrElse(string.Empty);
|
||||||
|
|
||||||
|
|
||||||
[JsonIgnore] public string IdString => Id.ToString("x16");
|
[JsonIgnore] public string IdString => Id.ToString("x16");
|
||||||
|
|
||||||
|
|||||||
@@ -60,10 +60,21 @@ namespace Ryujinx.Ava.Utilities.Compat
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static CompatibilityEntry Find(string titleId)
|
||||||
|
=> Entries.FirstOrDefault(x => x.TitleId.HasValue && x.TitleId.Value.EqualsIgnoreCase(titleId));
|
||||||
|
|
||||||
|
public static CompatibilityEntry Find(ulong titleId)
|
||||||
|
=> Find(titleId.ToString("X16"));
|
||||||
|
|
||||||
public static LocaleKeys? GetStatus(string titleId)
|
public static LocaleKeys? GetStatus(string titleId)
|
||||||
=> Entries.FirstOrDefault(x => x.TitleId.HasValue && x.TitleId.Value.EqualsIgnoreCase(titleId))?.Status;
|
=> Find(titleId)?.Status;
|
||||||
|
|
||||||
public static LocaleKeys? GetStatus(ulong titleId) => GetStatus(titleId.ToString("X16"));
|
public static LocaleKeys? GetStatus(ulong titleId) => GetStatus(titleId.ToString("X16"));
|
||||||
|
|
||||||
|
public static string GetLabels(string titleId)
|
||||||
|
=> Find(titleId)?.FormattedIssueLabels;
|
||||||
|
|
||||||
|
public static string GetLabels(ulong titleId) => GetLabels(titleId.ToString("X16"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public class CompatibilityEntry
|
public class CompatibilityEntry
|
||||||
|
|||||||
@@ -213,20 +213,20 @@ namespace Ryujinx.Ava.Utilities.PlayReport
|
|||||||
|
|
||||||
switch (Formatter)
|
switch (Formatter)
|
||||||
{
|
{
|
||||||
case SingleValueFormatter svf when data is MessagePackObject mpo:
|
case SingleValueFormatter svf when data is MessagePackObject match:
|
||||||
formattedValue = svf(new SingleValue(mpo) { Application = appMeta, PlayReport = playReport });
|
formattedValue = svf(
|
||||||
|
new SingleValue(match) { Application = appMeta, PlayReport = playReport }
|
||||||
|
);
|
||||||
return true;
|
return true;
|
||||||
case MultiValueFormatter mvf when data is List<MessagePackObject> messagePackObjects:
|
case MultiValueFormatter mvf when data is List<MessagePackObject> matches:
|
||||||
formattedValue =
|
formattedValue = mvf(
|
||||||
mvf(new MultiValue(messagePackObjects) { Application = appMeta, PlayReport = playReport });
|
new MultiValue(matches) { Application = appMeta, PlayReport = playReport }
|
||||||
|
);
|
||||||
return true;
|
return true;
|
||||||
case SparseMultiValueFormatter smvf when
|
case SparseMultiValueFormatter smvf when data is Dictionary<string, MessagePackObject> sparseMatches:
|
||||||
data is Dictionary<string, MessagePackObject> sparseMessagePackObjects:
|
formattedValue = smvf(
|
||||||
formattedValue =
|
new SparseMultiValue(sparseMatches) { Application = appMeta, PlayReport = playReport }
|
||||||
smvf(new SparseMultiValue(sparseMessagePackObjects)
|
);
|
||||||
{
|
|
||||||
Application = appMeta, PlayReport = playReport
|
|
||||||
});
|
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
throw new InvalidOperationException("Formatter delegate is not of a known type!");
|
throw new InvalidOperationException("Formatter delegate is not of a known type!");
|
||||||
|
|||||||
Reference in New Issue
Block a user