Compare commits

...
6 Commits
Author SHA1 Message Date
KeatonTheBotandLotP 3f62488b26 SDL: Update game controller database on launch (#177)
Expands upon existing custom SDL mappings functionality: b82e789d4f

Downloads an updated game controller database (gamecontrollerdb.txt) on launch, which only updates when a new file is available. This keeps the database updated between SDL releases.

Reviewed-on: https://git.ryujinx.app/projects/Ryubing/pulls/177
2026-07-30 21:18:39 +00:00
MabelandLotP e3940abe01 Threaded Renderer Crash, Threaded Renderer Index Desync, Logger Disposal (#174)
These 3 are all fixes made by LotP, who said they just didn't feel like making the PR atm and that someone else could do it if they wanted to (https://discord.com/channels/1294443224030511104/1295891559056674816/1531034369232211968)

Addresses these 3 specific issues:
1. Toggling the hidden console tries to assign a new console logger, which is also done on setting initial state, but at that point a logger already exists so it just returns, except previously it did not dispose of the new logger.
2. The threaded renderer would sometimes just crash when emulation is stopped, the fix being to make sure the backend render thread is joined before touching common objects
3. The disposing workflow of other threads than the main GPU adds commands to the command queue, causing a problem where the index can get desynced because of race conditions

Reviewed-on: https://git.ryujinx.app/projects/Ryubing/pulls/174
2026-07-27 20:03:45 +00:00
LotPandLotP1 a82350bb77 mono-jit-v2 (#173)
second attempt at single layer address tables (first attempt https://git.ryujinx.app/projects/Ryubing/pulls/167).
should work with all games now.

Co-authored-by: LotP1 <68976644+LotP1@users.noreply.github.com>
Reviewed-on: https://git.ryujinx.app/projects/Ryubing/pulls/173
2026-07-17 12:57:07 +00:00
LotPandLotP1 deb5dca420 Revert "mono-jit (#167)" (#172)
This reverts the mono jit PR.
It had several issues that i haven't had time to fix yet.
Expect mono jit v2 in the future with said fixes.

JitCache alignement fixes are still included/fixed.

Co-authored-by: LotP1 <68976644+LotP1@users.noreply.github.com>
Reviewed-on: https://git.ryujinx.app/projects/Ryubing/pulls/172
2026-07-14 10:23:16 +00:00
Maxandsh0inx 97738fdc51 Updated dotnet, dependencies, and action cache/checkout (#171)
Reviewed-on: https://git.ryujinx.app/projects/Ryubing/pulls/171
2026-07-14 00:08:34 +00:00
Maxandsh0inx dc06d0ded6 Refactored Directory.Packages.Props and updated dependencies (#170)
Updated packages:
- DynamicData | 9.4.31 -> 9.4.33
- Humanizer | 2.14.1 -> 3.0.10
- System.IO.Hashing | 9.0.15 -> 10.0.9
- Ryujinx.SDL3-CS | 2026.501.0 -> 2026.707.0

These packages were not updated because they were updated in !155
- Microsoft.NET.Test.SDK
- NUnit
- NUnit3TestAdapter

These packages were not updated because they rely !82 and !155 changes.
- SkiaSharp.NativeAssets
  - Should hopefully resolve issues with missing FontConfig on NixOS once it's updated.

Added rules to dependency management:
- Packages are to be on the latest stable version unless we require improved functionality from in-dev packages or regressions occur in future releases. This is to minimize issues with dependencies and improve reliability, speed, security, etc. Renovate can take care of smaller updates automatically, but all major version changes must be reviewed before committing.
- Packages are to be listed based on category, then alphabetically.
- Spacing is applied based on judgement, but generally speaking packages that are a part of a larger framework or must be updated together (or are misc. in that category) are grouped together.

Added TODO items based on necessary refactors.
- Avalonia 12 | addressed in !82.
- SkiaSharp
- Microsoft.CodeAnalysis.Analyzers
- OpenTk.Audio

Made it very clear not to touch Unicorn. !155 for details.

Reviewed-on: https://git.ryujinx.app/projects/Ryubing/pulls/170
2026-07-11 21:50:54 +00:00
37 changed files with 534 additions and 443 deletions
+4 -4
View File
@@ -35,7 +35,7 @@ jobs:
fail-fast: false fail-fast: false
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v7
- uses: actions/setup-dotnet@v5 - uses: actions/setup-dotnet@v5
with: with:
@@ -66,7 +66,7 @@ jobs:
if: forgejo.event_name == 'pull_request' if: forgejo.event_name == 'pull_request'
- name: 'Cache: ~/.nuget/packages' - name: 'Cache: ~/.nuget/packages'
uses: actions/cache@v5 uses: actions/cache@v6
with: with:
path: | path: |
~/.nuget/packages ~/.nuget/packages
@@ -148,7 +148,7 @@ jobs:
configuration: [ Release ] configuration: [ Release ]
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v7
- uses: actions/setup-dotnet@v5 - uses: actions/setup-dotnet@v5
with: with:
@@ -185,7 +185,7 @@ jobs:
if: forgejo.event_name == 'pull_request' if: forgejo.event_name == 'pull_request'
- name: 'Cache: ~/.nuget/packages' - name: 'Cache: ~/.nuget/packages'
uses: actions/cache@v5 uses: actions/cache@v6
with: with:
path: | path: |
~/.nuget/packages ~/.nuget/packages
+3 -3
View File
@@ -36,7 +36,7 @@ jobs:
- { name: linux-x64, os: ghcr.io/catthehacker/ubuntu:act-latest, zip_os_name: linux_x64 } - { name: linux-x64, os: ghcr.io/catthehacker/ubuntu:act-latest, zip_os_name: linux_x64 }
- { name: linux-arm64, os: ghcr.io/catthehacker/ubuntu:act-latest, zip_os_name: linux_arm64 } - { name: linux-arm64, os: ghcr.io/catthehacker/ubuntu:act-latest, zip_os_name: linux_arm64 }
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v7
- uses: actions/setup-dotnet@v5 - uses: actions/setup-dotnet@v5
with: with:
@@ -151,7 +151,7 @@ jobs:
container: container:
image: ghcr.io/catthehacker/ubuntu:act-latest image: ghcr.io/catthehacker/ubuntu:act-latest
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v7
- uses: actions/setup-dotnet@v5 - uses: actions/setup-dotnet@v5
with: with:
@@ -217,7 +217,7 @@ jobs:
- macos_release - macos_release
- release - release
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v7
- name: Install GLI - name: Install GLI
uses: actions/setup-gli@v1 uses: actions/setup-gli@v1
+1 -1
View File
@@ -10,7 +10,7 @@ jobs:
steps: steps:
# Grab sources to get latest labeler.yml # Grab sources to get latest labeler.yml
- name: Fetch sources - name: Fetch sources
uses: actions/checkout@v6 uses: actions/checkout@v7
with: with:
# Ensure we pin the source origin as pull_request_target run under forks. # Ensure we pin the source origin as pull_request_target run under forks.
fetch-depth: 0 fetch-depth: 0
+3 -3
View File
@@ -30,7 +30,7 @@ jobs:
- { name: linux-x64, os: ghcr.io/catthehacker/ubuntu:act-latest, zip_os_name: linux_x64 } - { name: linux-x64, os: ghcr.io/catthehacker/ubuntu:act-latest, zip_os_name: linux_x64 }
- { name: linux-arm64, os: ghcr.io/catthehacker/ubuntu:act-latest, zip_os_name: linux_arm64 } - { name: linux-arm64, os: ghcr.io/catthehacker/ubuntu:act-latest, zip_os_name: linux_arm64 }
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v7
- uses: actions/setup-dotnet@v5 - uses: actions/setup-dotnet@v5
with: with:
@@ -145,7 +145,7 @@ jobs:
name: Release MacOS universal name: Release MacOS universal
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v7
- uses: actions/setup-dotnet@v5 - uses: actions/setup-dotnet@v5
with: with:
@@ -210,7 +210,7 @@ jobs:
- macos_release - macos_release
- release - release
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v7
- name: Install GLI - name: Install GLI
uses: actions/setup-gli@v1 uses: actions/setup-gli@v1
+84 -47
View File
@@ -3,65 +3,102 @@
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally> <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<!-- Packages included in Ryujinx should be the latest stable releases -->
<!-- unless otherwise required for improved functionality or regressions ocurred. -->
<!-- Packages are listed by module/function first, then alphabetical order. -->
<!-- UI -->
<!-- TODO: Avalonia >= 12.0.0 requires refactoring -->
<PackageVersion Include="Avalonia" Version="11.3.18" /> <PackageVersion Include="Avalonia" Version="11.3.18" />
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.3.13" /> <PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.3.13" />
<PackageVersion Include="Avalonia.Desktop" Version="11.3.18" /> <PackageVersion Include="Avalonia.Desktop" Version="11.3.18" />
<!-- TODO: Replace Avalonia.Diagnostics (Deprecated). -->
<!-- <PackageVersion Include="AvaloniaUI.DiagnosticsSupport" Version="2.2.3" /> -->
<PackageVersion Include="Avalonia.Diagnostics" Version="11.3.18" /> <PackageVersion Include="Avalonia.Diagnostics" Version="11.3.18" />
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="11.3.18" /> <PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="11.3.18" />
<PackageVersion Include="SharpCompress" Version="0.49.1" />
<PackageVersion Include="Svg.Controls.Avalonia" Version="11.3.9.5" /> <PackageVersion Include="FluentAvaloniaUI" Version="2.5.1" />
<PackageVersion Include="Svg.Controls.Skia.Avalonia" Version="11.3.9.5" />
<PackageVersion Include="Microsoft.Build.Framework" Version="17.11.4" />
<PackageVersion Include="Microsoft.Build.Utilities.Core" Version="17.12.50" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.4" />
<PackageVersion Include="Projektanker.Icons.Avalonia" Version="9.6.2" /> <PackageVersion Include="Projektanker.Icons.Avalonia" Version="9.6.2" />
<PackageVersion Include="Projektanker.Icons.Avalonia.FontAwesome" Version="9.6.2" /> <PackageVersion Include="Projektanker.Icons.Avalonia.FontAwesome" Version="9.6.2" />
<PackageVersion Include="Projektanker.Icons.Avalonia.MaterialDesign" Version="9.6.2" /> <PackageVersion Include="Projektanker.Icons.Avalonia.MaterialDesign" Version="9.6.2" />
<PackageVersion Include="Ryujinx.SDL3-CS" Version="2026.501.0" />
<PackageVersion Include="CommandLineParser" Version="2.9.1" /> <!-- TODO: SkiaSharp > 2.88.9 requires refactoring -->
<PackageVersion Include="CommunityToolkit.Mvvm" Version="8.4.2" />
<PackageVersion Include="Concentus" Version="2.2.2" />
<PackageVersion Include="DiscordRichPresence" Version="1.6.1.70" />
<PackageVersion Include="DynamicData" Version="9.4.31" />
<PackageVersion Include="FluentAvaloniaUI" Version="2.5.1" />
<PackageVersion Include="Humanizer" Version="2.14.1" />
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2" />
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.19.1" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.1" />
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
<PackageVersion Include="NetCoreServer" Version="8.0.7" />
<PackageVersion Include="NUnit" Version="3.14.0" />
<PackageVersion Include="NUnit3TestAdapter" Version="4.6.0" />
<PackageVersion Include="OpenTK.Core" Version="4.9.4" />
<PackageVersion Include="OpenTK.Graphics" Version="4.9.4" />
<!-- OpenTk.Audio.OpenAL has moved to OpenTk.Audio -->
<!--<PackageVersion Include="OpenTK.Audio" Version="5.0.0-pre.15" />-->
<PackageVersion Include="OpenTK.Audio.OpenAL" Version="4.9.4" />
<PackageVersion Include="Open.NAT.Core" Version="2.1.0.5" />
<!-- Ryujinx.Audio.OpenAL.Dependencies is from the original project, last updated 12/30/20 -->
<!--<PackageVersion Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" />-->
<PackageVersion Include="Ryujinx.Audio.OpenAL" Version="1.25.2" />
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies.AllArch" Version="6.1.4-build6" />
<PackageVersion Include="Ryujinx.Graphics.Vulkan.MoltenVK" Version="1.4.2-ryujinx.4" />
<PackageVersion Include="Ryujinx.LibHac" Version="0.21.0-alpha.133" />
<PackageVersion Include="Ryujinx.UpdateClient" Version="2.0.6" />
<PackageVersion Include="Ryujinx.Systems.Update.Common" Version="2.0.6" />
<PackageVersion Include="Gommon" Version="2.8.1.2" />
<PackageVersion Include="securifybv.ShellLink" Version="0.1.0" />
<PackageVersion Include="Sep" Version="0.15.0" />
<PackageVersion Include="SharpZipLib" Version="1.4.2" />
<PackageVersion Include="Silk.NET.Shaderc" Version="2.23.0" />
<PackageVersion Include="Silk.NET.Vulkan" Version="2.23.0" />
<PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.23.0" />
<PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.23.0" />
<PackageVersion Include="SkiaSharp" Version="2.88.9" /> <PackageVersion Include="SkiaSharp" Version="2.88.9" />
<PackageVersion Include="SkiaSharp.NativeAssets.Win32" Version="2.88.9" /> <PackageVersion Include="SkiaSharp.NativeAssets.Win32" Version="2.88.9" />
<PackageVersion Include="SkiaSharp.NativeAssets.macOS" Version="2.88.9" /> <PackageVersion Include="SkiaSharp.NativeAssets.macOS" Version="2.88.9" />
<PackageVersion Include="SkiaSharp.NativeAssets.Linux.NoDependencies" Version="2.88.9" /> <PackageVersion Include="SkiaSharp.NativeAssets.Linux.NoDependencies" Version="2.88.9" />
<PackageVersion Include="SPB" Version="0.0.4-build32" />
<PackageVersion Include="System.IO.Hashing" Version="9.0.15" /> <PackageVersion Include="Svg.Controls.Avalonia" Version="11.3.9.5" />
<PackageVersion Include="Svg.Controls.Skia.Avalonia" Version="11.3.9.5" />
<!-- Building & Packaging -->
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
<PackageVersion Include="CommunityToolkit.Mvvm" Version="8.4.2" />
<PackageVersion Include="DynamicData" Version="9.4.33" />
<PackageVersion Include="Gommon" Version="2.8.1.2" />
<PackageVersion Include="Humanizer" Version="3.0.10" />
<!-- TODO: Microsoft.CodeAnalysis.Analyzers > 3.3.4 needs refactoring -->
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2" /> <!-- Must be updated with Analyzers -->
<PackageVersion Include="Microsoft.Build.Framework" Version="17.11.4" />
<PackageVersion Include="Microsoft.Build.Utilities.Core" Version="17.12.50" />
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.19.1" />
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.1" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
<PackageVersion Include="NetCoreServer" Version="8.0.7" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.4" />
<PackageVersion Include="NUnit" Version="3.14.0" />
<PackageVersion Include="NUnit3TestAdapter" Version="4.6.0" />
<PackageVersion Include="securifybv.ShellLink" Version="0.1.0" />
<PackageVersion Include="Sep" Version="0.15.0" />
<PackageVersion Include="SharpCompress" Version="0.50.0" />
<PackageVersion Include="SharpZipLib" Version="1.4.2" />
<PackageVersion Include="System.IO.Hashing" Version="10.0.9" />
<!--Don't touch this, we're freezing the Unicorn package due to an issue with bindngs.-->
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.1.0" /> <PackageVersion Include="UnicornEngine.Unicorn" Version="2.1.0" />
<!-- Internal Dependencies -->
<!-- Ryujinx.Audio.OpenAL.Dependencies is from the original project, last updated 12/30/20 -->
<!--<PackageVersion Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" />-->
<PackageVersion Include="Ryujinx.Audio.OpenAL" Version="1.25.2" />
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies.AllArch" Version="6.1.4-build6" />
<PackageVersion Include="Ryujinx.Graphics.Vulkan.MoltenVK" Version="1.4.2-ryujinx.5" />
<PackageVersion Include="Ryujinx.LibHac" Version="0.21.0-alpha.133" />
<PackageVersion Include="Ryujinx.SDL3-CS" Version="2026.707.0" />
<PackageVersion Include="Ryujinx.Systems.Update.Common" Version="2.0.6" />
<PackageVersion Include="Ryujinx.UpdateClient" Version="2.0.6" />
<PackageVersion Include="SPB" Version="0.0.4-build32" />
<!-- External Dependencies -->
<PackageVersion Include="Concentus" Version="2.2.2" />
<PackageVersion Include="DiscordRichPresence" Version="1.6.1.70" />
<PackageVersion Include="OpenTK.Core" Version="4.9.4" />
<PackageVersion Include="OpenTK.Graphics" Version="4.9.4" />
<!-- TODO: OpenTk.Audio.OpenAL has moved to OpenTk.Audio -->
<!-- OpenTk.Audio.OpenAL > 4.9.4 requires refactoring -->
<!--<PackageVersion Include="OpenTK.Audio" Version="5.0.0-pre.16" />-->
<PackageVersion Include="OpenTK.Audio.OpenAL" Version="4.9.4" />
<!-- TODO: Consider replacing with SharpOpenNat. -->
<PackageVersion Include="Open.Nat.V2" Version="3.2.1" />
<PackageVersion Include="Silk.NET.Shaderc" Version="2.23.0" />
<PackageVersion Include="Silk.NET.Vulkan" Version="2.23.0" />
<PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.23.0" />
<PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.23.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"sdk": { "sdk": {
"version": "10.0.100", "version": "10.0.301",
"rollForward": "latestFeature" "rollForward": "latestFeature"
} }
} }
@@ -19,22 +19,22 @@ namespace ARMeilleure.Common
new( 7, 8), new( 7, 8),
new( 1, 6) new( 1, 6)
]; ];
private static readonly AddressTableLevel[] _monoSparse64Bit = private static readonly AddressTableLevel[] _levels64BitMono =
[ [
new( 2, 37) new( 2, 37)
]; ];
private static readonly AddressTableLevel[] _monoSparse32Bit = private static readonly AddressTableLevel[] _levels32BitMono =
[ [
new( 1, 31) new( 1, 31)
]; ];
public static AddressTableLevel[] GetArmPreset(bool for64Bits, bool sparse) public static AddressTableLevel[] GetArmPreset(bool for64Bits, bool mono)
{ {
if (sparse) if (mono)
{ {
return for64Bits ? _monoSparse64Bit : _monoSparse32Bit; return for64Bits ? _levels64BitMono : _levels32BitMono;
} }
else else
{ {
@@ -0,0 +1,8 @@
namespace ARMeilleure.Common
{
public enum AddressTableType
{
Default,
Sparse
}
}
+9
View File
@@ -53,5 +53,14 @@ namespace ARMeilleure.Common
{ {
return (bits >> shift) | (bits << (size - shift)); return (bits >> shift) | (bits << (size - shift));
} }
public static T AlignUp<T>(T value, T size) where T : IBinaryInteger<T>
=> (value + (size - T.One)) & -size;
public static T AlignDown<T>(T value, T size) where T : IBinaryInteger<T>
=> value & -size;
public static T DivRoundUp<T>(T value, T dividend) where T : IBinaryInteger<T>
=> (value + (dividend - T.One)) / dividend;
} }
} }
+10 -4
View File
@@ -5,11 +5,10 @@ namespace ARMeilleure.Common
public interface IAddressTable<TEntry> : IDisposable where TEntry : unmanaged public interface IAddressTable<TEntry> : IDisposable where TEntry : unmanaged
{ {
/// <summary> /// <summary>
/// True if the address table's bottom level is sparsely mapped. /// Gets the <see cref="AddressTableType"/> of the <see cref="IAddressTable{TEntry}"/> instance.
/// This also ensures the second bottom level is filled with a dummy page rather than 0.
/// </summary> /// </summary>
bool Sparse { get; } AddressTableType TableType { get; }
/// <summary> /// <summary>
/// Gets the bits used by the <see cref="Levels"/> of the <see cref="IAddressTable{TEntry}"/> instance. /// Gets the bits used by the <see cref="Levels"/> of the <see cref="IAddressTable{TEntry}"/> instance.
/// </summary> /// </summary>
@@ -31,6 +30,13 @@ namespace ARMeilleure.Common
/// <exception cref="ObjectDisposedException"><see cref="EntryTable{TEntry}"/> instance was disposed</exception> /// <exception cref="ObjectDisposedException"><see cref="EntryTable{TEntry}"/> instance was disposed</exception>
nint Base { get; } nint Base { get; }
/// <summary>
/// Signal that the given code range exists.
/// </summary>
/// <param name="address">Guest code range address</param>
/// <param name="size">Guest code range size</param>
void SignalCodeRange(ulong address, ulong size);
/// <summary> /// <summary>
/// Determines if the specified <paramref name="address"/> is in the range of the /// Determines if the specified <paramref name="address"/> is in the range of the
/// <see cref="IAddressTable{TEntry}"/>. /// <see cref="IAddressTable{TEntry}"/>.
@@ -236,7 +236,7 @@ namespace ARMeilleure.Instructions
hostAddress = context.Load(OperandType.I64, hostAddressAddr); hostAddress = context.Load(OperandType.I64, hostAddressAddr);
} }
else if (table.Sparse) else if (table.TableType == AddressTableType.Sparse)
{ {
// Inline table lookup. Only enabled when the sparse function table is enabled with 1 level. // Inline table lookup. Only enabled when the sparse function table is enabled with 1 level.
// Deliberately attempts to avoid branches. // Deliberately attempts to avoid branches.
@@ -260,8 +260,8 @@ namespace ARMeilleure.Instructions
else else
{ {
hostAddress = !context.HasPtc ? hostAddress = !context.HasPtc ?
Const((long)context.Stubs.DispatchStub) : Const(context.Stubs.DispatchStub) :
Const((long)context.Stubs.DispatchStub, Ptc.DispatchStubSymbol); Const(context.Stubs.DispatchStub, Ptc.DispatchStubSymbol);
} }
if (isJump) if (isJump)
@@ -132,7 +132,7 @@ namespace ARMeilleure.Translation.Cache
int regionEnd = endOffs % (int)CacheSize == 0 int regionEnd = endOffs % (int)CacheSize == 0
? (((int)CacheSize) + _pageMask) & ~_pageMask ? (((int)CacheSize) + _pageMask) & ~_pageMask
: ((endOffs % (int)CacheSize) + _pageMask) & ~_pageMask; : ((endOffs % (int)CacheSize) + _pageMask) & ~_pageMask;
GetRegion(offset).Block.MapAsRwx((ulong)regionStart, (ulong)(regionEnd - regionStart)); GetRegion(offset).Block.MapAsRwx((ulong)regionStart, (ulong)(regionEnd - regionStart));
} }
+1 -1
View File
@@ -34,7 +34,7 @@ namespace ARMeilleure.Translation.PTC
private const string OuterHeaderMagicString = "PTCohd\0\0"; private const string OuterHeaderMagicString = "PTCohd\0\0";
private const string InnerHeaderMagicString = "PTCihd\0\0"; private const string InnerHeaderMagicString = "PTCihd\0\0";
private const uint InternalVersion = 7018; //! To be incremented manually for each change to the ARMeilleure project. private const uint InternalVersion = 7020; //! To be incremented manually for each change to the ARMeilleure project.
private const string ActualDir = "0"; private const string ActualDir = "0";
private const string BackupDir = "1"; private const string BackupDir = "1";
+3 -1
View File
@@ -28,7 +28,7 @@ namespace ARMeilleure.Translation
private readonly Ptc _ptc; private readonly Ptc _ptc;
internal TranslatorCache<TranslatedFunction> Functions { get; } internal TranslatorCache<TranslatedFunction> Functions { get; }
internal IAddressTable<ulong> FunctionTable { get; } public IAddressTable<ulong> FunctionTable { get; }
internal EntryTable<uint> CountTable { get; } internal EntryTable<uint> CountTable { get; }
internal TranslatorStubs Stubs { get; } internal TranslatorStubs Stubs { get; }
internal TranslatorQueue Queue { get; } internal TranslatorQueue Queue { get; }
@@ -53,7 +53,9 @@ namespace ARMeilleure.Translation
CountTable = new EntryTable<uint>(); CountTable = new EntryTable<uint>();
Functions = new TranslatorCache<TranslatedFunction>(); Functions = new TranslatorCache<TranslatedFunction>();
FunctionTable = functionTable; FunctionTable = functionTable;
Stubs = new TranslatorStubs(JitCache, FunctionTable); Stubs = new TranslatorStubs(JitCache, FunctionTable);
FunctionTable.Fill = (ulong)Stubs.SlowDispatchStub; FunctionTable.Fill = (ulong)Stubs.SlowDispatchStub;
@@ -161,7 +161,7 @@ namespace ARMeilleure.Translation
context.BranchIfTrue(lblFallback, masked); context.BranchIfTrue(lblFallback, masked);
Operand index = default; Operand index = default;
Operand page = Const(_functionTable.Base); Operand page = Const((long)_functionTable.Base);
for (int i = 0; i < _functionTable.Levels.Length; i++) for (int i = 0; i < _functionTable.Levels.Length; i++)
{ {
+1
View File
@@ -162,6 +162,7 @@ namespace Ryujinx.Common.Logging
{ {
if (_logTargets.Any(t => t.Name == target.Name)) if (_logTargets.Any(t => t.Name == target.Name))
{ {
target.Dispose();
return; return;
} }
+38 -319
View File
@@ -1,15 +1,9 @@
using ARMeilleure.Memory; using ARMeilleure.Common;
using Ryujinx.Common;
using Ryujinx.Cpu.Signal;
using Ryujinx.Memory;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading;
using static Ryujinx.Cpu.MemoryEhMeilleure;
namespace ARMeilleure.Common namespace Ryujinx.Cpu
{ {
/// <summary> /// <summary>
/// Represents a table of guest address to a value. /// Represents a table of guest address to a value.
@@ -17,85 +11,12 @@ namespace ARMeilleure.Common
/// <typeparam name="TEntry">Type of the value</typeparam> /// <typeparam name="TEntry">Type of the value</typeparam>
public unsafe class AddressTable<TEntry> : IAddressTable<TEntry> where TEntry : unmanaged public unsafe class AddressTable<TEntry> : IAddressTable<TEntry> where TEntry : unmanaged
{ {
/// <summary>
/// Represents a page of the address table.
/// </summary>
private readonly struct AddressTablePage
{
/// <summary>
/// True if the allocation belongs to a sparse block, false otherwise.
/// </summary>
public readonly bool IsSparse;
/// <summary>
/// Base address for the page.
/// </summary>
public readonly nint Address;
public AddressTablePage(bool isSparse, nint address)
{
IsSparse = isSparse;
Address = address;
}
}
/// <summary>
/// A sparsely mapped block of memory with a signal handler to map pages as they're accessed.
/// </summary>
private readonly struct TableSparseBlock : IDisposable
{
public readonly SparseMemoryBlock Block;
private readonly TrackingEventDelegate _trackingEvent;
public TableSparseBlock(ulong size, Action<nint> ensureMapped, PageInitDelegate pageInit)
{
SparseMemoryBlock block = new(size, pageInit, null);
_trackingEvent = (address, size, write) =>
{
ulong pointer = (ulong)block.Block.Pointer + address;
ensureMapped((nint)pointer);
return pointer;
};
bool added = NativeSignalHandler.AddTrackedRegion(
(nuint)block.Block.Pointer,
(nuint)(block.Block.Pointer + (nint)block.Block.Size),
Marshal.GetFunctionPointerForDelegate(_trackingEvent));
if (!added)
{
throw new InvalidOperationException("Number of allowed tracked regions exceeded.");
}
Block = block;
}
public void Dispose()
{
NativeSignalHandler.RemoveTrackedRegion((nuint)Block.Block.Pointer);
Block.Dispose();
}
}
private bool _disposed; private bool _disposed;
private TEntry** _table; private TEntry** _table;
private TEntry* _sparseTable; private readonly List<nint> _pages;
private readonly List<AddressTablePage> _pages;
private TEntry _fill;
private MemoryBlock _sparseFill; /// <inheritdoc/>
private SparseMemoryBlock _fillBottomLevel; public AddressTableType TableType => AddressTableType.Default;
private TEntry* _fillBottomLevelPtr;
private readonly List<TableSparseBlock> _sparseReserved;
private readonly ReaderWriterLockSlim _sparseLock;
private ulong _sparseBlockSize;
private ulong _sparseReservedOffset;
public bool Sparse { get; }
/// <inheritdoc/> /// <inheritdoc/>
public ulong Mask { get; } public ulong Mask { get; }
@@ -104,17 +25,7 @@ namespace ARMeilleure.Common
public AddressTableLevel[] Levels { get; } public AddressTableLevel[] Levels { get; }
/// <inheritdoc/> /// <inheritdoc/>
public TEntry Fill public TEntry Fill { get; set; }
{
get
{
return _fill;
}
set
{
UpdateFill(value);
}
}
/// <inheritdoc/> /// <inheritdoc/>
public nint Base public nint Base
@@ -123,16 +34,9 @@ namespace ARMeilleure.Common
{ {
ObjectDisposedException.ThrowIf(_disposed, this); ObjectDisposedException.ThrowIf(_disposed, this);
if (Sparse) lock (_pages)
{ {
return (nint)_sparseTable; return (nint)GetRootPage();
}
else
{
lock (_pages)
{
return (nint)GetRootPage();
}
} }
} }
} }
@@ -142,14 +46,13 @@ namespace ARMeilleure.Common
/// <see cref="AddressTableLevel"/>. /// <see cref="AddressTableLevel"/>.
/// </summary> /// </summary>
/// <param name="levels">Levels for the address table</param> /// <param name="levels">Levels for the address table</param>
/// <param name="sparse">True if the bottom page should be sparsely mapped</param>
/// <exception cref="ArgumentNullException"><paramref name="levels"/> is null</exception> /// <exception cref="ArgumentNullException"><paramref name="levels"/> is null</exception>
/// <exception cref="ArgumentException">Length of <paramref name="levels"/> is less than 2</exception> /// <exception cref="ArgumentException">Length of <paramref name="levels"/> is less than 2</exception>
public AddressTable(AddressTableLevel[] levels, bool sparse) public AddressTable(AddressTableLevel[] levels)
{ {
ArgumentNullException.ThrowIfNull(levels); ArgumentNullException.ThrowIfNull(levels);
_pages = new List<AddressTablePage>(capacity: 16); _pages = new List<nint>(capacity: 16);
Levels = levels; Levels = levels;
Mask = 0; Mask = 0;
@@ -158,15 +61,6 @@ namespace ARMeilleure.Common
{ {
Mask |= level.Mask; Mask |= level.Mask;
} }
Sparse = sparse;
if (sparse)
{
// If the address table is sparse, allocate a fill block
_sparseReserved = [];
_sparseLock = new ReaderWriterLockSlim();
}
} }
/// <summary> /// <summary>
@@ -174,29 +68,10 @@ namespace ARMeilleure.Common
/// Selects the best table structure for A32/A64, taking into account the selected memory manager type. /// Selects the best table structure for A32/A64, taking into account the selected memory manager type.
/// </summary> /// </summary>
/// <param name="for64Bits">True if the guest is A64, false otherwise</param> /// <param name="for64Bits">True if the guest is A64, false otherwise</param>
/// <param name="type">Memory manager type</param>
/// <returns>An <see cref="AddressTable{TEntry}"/> for ARM function lookup</returns> /// <returns>An <see cref="AddressTable{TEntry}"/> for ARM function lookup</returns>
public static AddressTable<TEntry> CreateForArm(bool for64Bits, MemoryManagerType type) public static AddressTable<TEntry> CreateForArm(bool for64Bits)
{ {
// Assume software memory means that we don't want to use any signal handlers. return new AddressTable<TEntry>(AddressTablePresets.GetArmPreset(for64Bits, false));
bool sparse = type is not MemoryManagerType.SoftwareMmu and not MemoryManagerType.SoftwarePageTable;
return new AddressTable<TEntry>(AddressTablePresets.GetArmPreset(for64Bits, sparse), sparse);
}
/// <summary>
/// Update the fill value for the bottom level of the table.
/// </summary>
/// <param name="fillValue">New fill value</param>
private void UpdateFill(TEntry fillValue)
{
if (_sparseFill != null)
{
Span<byte> span = _sparseFill.GetSpan(0, (int)_sparseFill.Size);
MemoryMarshal.Cast<byte, TEntry>(span).Fill(fillValue);
}
_fill = fillValue;
} }
/// <summary> /// <summary>
@@ -204,28 +79,7 @@ namespace ARMeilleure.Common
/// </summary> /// </summary>
/// <param name="address"></param> /// <param name="address"></param>
/// <param name="size"></param> /// <param name="size"></param>
public void SignalCodeRange(ulong address, ulong size) public void SignalCodeRange(ulong address, ulong size) { }
{
AddressTableLevel bottom = Levels.Last();
ulong entries = size >> bottom.Index;
if (Sparse)
{
ulong bottomLevelSize = (ulong)BitUtils.Pow2RoundUp((int)entries) * (ulong)sizeof(TEntry);
_sparseFill = new MemoryBlock(bottomLevelSize, MemoryAllocationFlags.Mirrorable);
_fillBottomLevel = new SparseMemoryBlock(bottomLevelSize, null, _sparseFill);
_fillBottomLevelPtr = (TEntry*)_fillBottomLevel.Block.Pointer;
_sparseBlockSize = bottomLevelSize;
_sparseTable = (TEntry*)Allocate((int)entries, Fill, leaf: true);
_sparseTable -= Levels.Last().GetValue(address);
}
}
/// <inheritdoc/> /// <inheritdoc/>
public bool IsValid(ulong address) public bool IsValid(ulong address)
@@ -243,26 +97,13 @@ namespace ARMeilleure.Common
throw new ArgumentException($"Address 0x{address:X} is not mapped onto the table.", nameof(address)); throw new ArgumentException($"Address 0x{address:X} is not mapped onto the table.", nameof(address));
} }
if (Sparse) lock (_pages)
{ {
long index = Levels.Last().GetValue(address); TEntry* page = GetPage(address);
EnsureMapped((nint)(_sparseTable + index));
return ref _sparseTable[index];
}
else
{
lock (_pages)
{
TEntry* page = GetPage(address);
long index = Levels.Last().GetValue(address); long index = Levels[^1].GetValue(address);
EnsureMapped((nint)(page + index)); return ref page[index];
return ref page[index];
}
} }
} }
@@ -278,19 +119,19 @@ namespace ARMeilleure.Common
for (int i = 0; i < Levels.Length - 1; i++) for (int i = 0; i < Levels.Length - 1; i++)
{ {
ref AddressTableLevel level = ref Levels[i]; ref AddressTableLevel level = ref Levels[i];
ref TEntry* nextPage = ref page[level.GetValue(address)]; ref TEntry* nextPage = ref page![level.GetValue(address)];
if (nextPage == null || nextPage == _fillBottomLevelPtr) if (nextPage == null)
{ {
ref AddressTableLevel nextLevel = ref Levels[i + 1]; ref AddressTableLevel nextLevel = ref Levels[i + 1];
if (i == Levels.Length - 2) if (i == Levels.Length - 2)
{ {
nextPage = (TEntry*)Allocate(1 << nextLevel.Length, Fill, leaf: true); nextPage = (TEntry*)Allocate(1 << nextLevel.Length, Fill);
} }
else else
{ {
nextPage = (TEntry*)Allocate(1 << nextLevel.Length, GetFillValue(i), leaf: false); nextPage = (TEntry*)Allocate(1 << nextLevel.Length, nint.Zero);
} }
} }
@@ -300,57 +141,6 @@ namespace ARMeilleure.Common
return (TEntry*)page; return (TEntry*)page;
} }
/// <summary>
/// Ensure the given pointer is mapped in any overlapping sparse reservations.
/// </summary>
/// <param name="ptr">Pointer to be mapped</param>
private void EnsureMapped(nint ptr)
{
if (Sparse)
{
// Check sparse allocations to see if the pointer is in any of them.
// Ensure the page is committed if there's a match.
_sparseLock.EnterReadLock();
try
{
foreach (TableSparseBlock reserved in _sparseReserved)
{
SparseMemoryBlock sparse = reserved.Block;
if (ptr >= sparse.Block.Pointer && ptr < sparse.Block.Pointer + (nint)sparse.Block.Size)
{
sparse.EnsureMapped((ulong)(ptr - sparse.Block.Pointer));
break;
}
}
}
finally
{
_sparseLock.ExitReadLock();
}
}
}
/// <summary>
/// Get the fill value for a non-leaf level of the table.
/// </summary>
/// <param name="level">Level to get the fill value for</param>
/// <returns>The fill value</returns>
private nint GetFillValue(int level)
{
if (_fillBottomLevel != null && level == Levels.Length - 2)
{
return (nint)_fillBottomLevelPtr;
}
else
{
return nint.Zero;
}
}
/// <summary> /// <summary>
/// Lazily initialize and get the root page of the <see cref="AddressTable{TEntry}"/>. /// Lazily initialize and get the root page of the <see cref="AddressTable{TEntry}"/>.
/// </summary> /// </summary>
@@ -360,91 +150,35 @@ namespace ARMeilleure.Common
if (_table == null) if (_table == null)
{ {
if (Levels.Length == 1) if (Levels.Length == 1)
_table = (TEntry**)Allocate(1 << Levels[0].Length, Fill, leaf: true); _table = (TEntry**)Allocate(1 << Levels[0].Length, Fill);
else else
_table = (TEntry**)Allocate(1 << Levels[0].Length, GetFillValue(0), leaf: false); _table = (TEntry**)Allocate(1 << Levels[0].Length, nint.Zero);
} }
return _table; return _table;
} }
/// <summary>
/// Initialize a leaf page with the fill value.
/// </summary>
/// <param name="page">Page to initialize</param>
private void InitLeafPage(Span<byte> page)
{
MemoryMarshal.Cast<byte, TEntry>(page).Fill(_fill);
}
/// <summary>
/// Reserve a new sparse block, and add it to the list.
/// </summary>
/// <returns>The new sparse block that was added</returns>
private TableSparseBlock ReserveNewSparseBlock()
{
TableSparseBlock block = new(_sparseBlockSize, EnsureMapped, InitLeafPage);
_sparseReserved.Add(block);
_sparseReservedOffset = 0;
return block;
}
/// <summary> /// <summary>
/// Allocates a block of memory of the specified type and length. /// Allocates a block of memory of the specified type and length.
/// </summary> /// </summary>
/// <typeparam name="T">Type of elements</typeparam> /// <typeparam name="T">Type of elements</typeparam>
/// <param name="length">Number of elements</param> /// <param name="length">Number of elements</param>
/// <param name="fill">Fill value</param> /// <param name="fill">Fill value</param>
/// <param name="leaf"><see langword="true"/> if leaf; otherwise <see langword="false"/></param>
/// <returns>Allocated block</returns> /// <returns>Allocated block</returns>
private nint Allocate<T>(int length, T fill, bool leaf) where T : unmanaged private nint Allocate<T>(int length, T fill) where T : unmanaged
{ {
int size = sizeof(T) * length; int size = sizeof(T) * length;
AddressTablePage page; nint address = (nint)NativeAllocator.Instance.Allocate((uint)size);
if (Sparse && leaf) Span<T> span = new((void*)address, length);
{ span.Fill(fill);
_sparseLock.EnterWriteLock();
SparseMemoryBlock block; _pages.Add(address);
if (_sparseReserved.Count == 0)
{
block = ReserveNewSparseBlock().Block;
}
else
{
block = _sparseReserved.Last().Block;
if (_sparseReservedOffset == block.Block.Size)
{
block = ReserveNewSparseBlock().Block;
}
}
page = new AddressTablePage(true, block.Block.Pointer + (nint)_sparseReservedOffset);
_sparseReservedOffset += (ulong)size;
_sparseLock.ExitWriteLock();
}
else
{
nint address = (nint)NativeAllocator.Instance.Allocate((uint)size);
page = new AddressTablePage(false, address);
Span<T> span = new((void*)page.Address, length);
span.Fill(fill);
}
_pages.Add(page);
//TranslatorEventSource.Log.AddressTableAllocated(size, leaf); //TranslatorEventSource.Log.AddressTableAllocated(size, leaf);
return page.Address; return address;
} }
/// <summary> /// <summary>
@@ -463,32 +197,17 @@ namespace ARMeilleure.Common
/// <param name="disposing"><see langword="true"/> to dispose managed resources also; otherwise just unmanaged resouces</param> /// <param name="disposing"><see langword="true"/> to dispose managed resources also; otherwise just unmanaged resouces</param>
protected virtual void Dispose(bool disposing) protected virtual void Dispose(bool disposing)
{ {
if (!_disposed) if (_disposed)
{ {
foreach (AddressTablePage page in _pages) return;
{
if (!page.IsSparse)
{
Marshal.FreeHGlobal(page.Address);
}
}
if (Sparse)
{
foreach (TableSparseBlock block in _sparseReserved)
{
block.Dispose();
}
_sparseReserved.Clear();
_fillBottomLevel.Dispose();
_sparseFill.Dispose();
_sparseLock.Dispose();
}
_disposed = true;
} }
foreach (nint page in _pages)
{
Marshal.FreeHGlobal(page);
}
_disposed = true;
} }
/// <summary> /// <summary>
+6 -4
View File
@@ -9,13 +9,15 @@ namespace Ryujinx.Cpu.Jit
{ {
private readonly ITickSource _tickSource; private readonly ITickSource _tickSource;
private readonly Translator _translator; private readonly Translator _translator;
private readonly AddressTable<ulong> _functionTable;
public JitCpuContext(ITickSource tickSource, IMemoryManager memory, bool for64Bit) public JitCpuContext(ITickSource tickSource, IMemoryManager memory, bool for64Bit)
{ {
_tickSource = tickSource; _tickSource = tickSource;
_functionTable = AddressTable<ulong>.CreateForArm(for64Bit, memory.Type);
_translator = new Translator(new JitMemoryAllocator(forJit: true), memory, _functionTable); bool sparse = memory.Type is not MemoryManagerType.SoftwareMmu and not MemoryManagerType.SoftwarePageTable;
IAddressTable<ulong> functionTable = sparse ? SparseAddressTable<ulong>.CreateForArm(for64Bit) : AddressTable<ulong>.CreateForArm(for64Bit);
_translator = new Translator(new JitMemoryAllocator(forJit: true), memory, functionTable);
if (memory.Type.IsHostMappedOrTracked) if (memory.Type.IsHostMappedOrTracked)
{ {
@@ -57,7 +59,7 @@ namespace Ryujinx.Cpu.Jit
/// <inheritdoc/> /// <inheritdoc/>
public void PrepareCodeRange(ulong address, ulong size) public void PrepareCodeRange(ulong address, ulong size)
{ {
_functionTable.SignalCodeRange(address, size); _translator.FunctionTable.SignalCodeRange(address, size);
_translator.PrepareCodeRange(address, size); _translator.PrepareCodeRange(address, size);
} }
@@ -13,7 +13,7 @@ namespace Ryujinx.Cpu.LightningJit
CpuPreset cpuPreset, CpuPreset cpuPreset,
IMemoryManager memoryManager, IMemoryManager memoryManager,
ulong address, ulong address,
AddressTable<ulong> funcTable, IAddressTable<ulong> funcTable,
nint dispatchStubPtr, nint dispatchStubPtr,
ExecutionMode executionMode, ExecutionMode executionMode,
Architecture targetArch) Architecture targetArch)
@@ -12,7 +12,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32
CpuPreset cpuPreset, CpuPreset cpuPreset,
IMemoryManager memoryManager, IMemoryManager memoryManager,
ulong address, ulong address,
AddressTable<ulong> funcTable, IAddressTable<ulong> funcTable,
nint dispatchStubPtr, nint dispatchStubPtr,
bool isThumb, bool isThumb,
Architecture targetArch) Architecture targetArch)
@@ -22,7 +22,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
public readonly RegisterAllocator RegisterAllocator; public readonly RegisterAllocator RegisterAllocator;
public readonly MemoryManagerType MemoryManagerType; public readonly MemoryManagerType MemoryManagerType;
public readonly TailMerger TailMerger; public readonly TailMerger TailMerger;
public readonly AddressTable<ulong> FuncTable; public readonly IAddressTable<ulong> FuncTable;
public readonly nint DispatchStubPointer; public readonly nint DispatchStubPointer;
private readonly RegisterSaveRestore _registerSaveRestore; private readonly RegisterSaveRestore _registerSaveRestore;
@@ -33,7 +33,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
RegisterAllocator registerAllocator, RegisterAllocator registerAllocator,
MemoryManagerType mmType, MemoryManagerType mmType,
TailMerger tailMerger, TailMerger tailMerger,
AddressTable<ulong> funcTable, IAddressTable<ulong> funcTable,
RegisterSaveRestore registerSaveRestore, RegisterSaveRestore registerSaveRestore,
nint dispatchStubPointer, nint dispatchStubPointer,
nint pageTablePointer) nint pageTablePointer)
@@ -225,7 +225,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
} }
} }
public static CompiledFunction Compile(CpuPreset cpuPreset, IMemoryManager memoryManager, ulong address, AddressTable<ulong> funcTable, nint dispatchStubPtr, bool isThumb) public static CompiledFunction Compile(CpuPreset cpuPreset, IMemoryManager memoryManager, ulong address, IAddressTable<ulong> funcTable, nint dispatchStubPtr, bool isThumb)
{ {
MultiBlock multiBlock = Decoder<InstEmit>.DecodeMulti(cpuPreset, memoryManager, address, isThumb); MultiBlock multiBlock = Decoder<InstEmit>.DecodeMulti(cpuPreset, memoryManager, address, isThumb);
@@ -133,7 +133,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
RegisterAllocator regAlloc, RegisterAllocator regAlloc,
TailMerger tailMerger, TailMerger tailMerger,
Action writeEpilogue, Action writeEpilogue,
AddressTable<ulong> funcTable, IAddressTable<ulong> funcTable,
nint funcPtr, nint funcPtr,
int spillBaseOffset, int spillBaseOffset,
uint nextAddress, uint nextAddress,
@@ -144,7 +144,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
int tempGuestAddress = -1; int tempGuestAddress = -1;
bool inlineLookup = guestAddress.Kind != OperandKind.Constant && bool inlineLookup = guestAddress.Kind != OperandKind.Constant &&
funcTable is { Sparse: true }; funcTable.TableType == AddressTableType.Sparse;
if (guestAddress.Kind == OperandKind.Constant) if (guestAddress.Kind == OperandKind.Constant)
{ {
@@ -12,7 +12,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm64
CpuPreset cpuPreset, CpuPreset cpuPreset,
IMemoryManager memoryManager, IMemoryManager memoryManager,
ulong address, ulong address,
AddressTable<ulong> funcTable, IAddressTable<ulong> funcTable,
nint dispatchStubPtr, nint dispatchStubPtr,
Architecture targetArch) Architecture targetArch)
{ {
@@ -18,7 +18,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
public readonly CodeWriter Writer; public readonly CodeWriter Writer;
public readonly RegisterAllocator RegisterAllocator; public readonly RegisterAllocator RegisterAllocator;
public readonly TailMerger TailMerger; public readonly TailMerger TailMerger;
public readonly AddressTable<ulong> FuncTable; public readonly IAddressTable<ulong> FuncTable;
public readonly nint DispatchStubPointer; public readonly nint DispatchStubPointer;
private readonly MultiBlock _multiBlock; private readonly MultiBlock _multiBlock;
@@ -31,7 +31,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
TailMerger tailMerger, TailMerger tailMerger,
RegisterSaveRestore registerSaveRestore, RegisterSaveRestore registerSaveRestore,
MultiBlock multiBlock, MultiBlock multiBlock,
AddressTable<ulong> funcTable, IAddressTable<ulong> funcTable,
nint dispatchStubPointer, nint dispatchStubPointer,
nint pageTablePointer) nint pageTablePointer)
{ {
@@ -303,7 +303,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
} }
} }
public static CompiledFunction Compile(CpuPreset cpuPreset, IMemoryManager memoryManager, ulong address, AddressTable<ulong> funcTable, nint dispatchStubPtr) public static CompiledFunction Compile(CpuPreset cpuPreset, IMemoryManager memoryManager, ulong address, IAddressTable<ulong> funcTable, nint dispatchStubPtr)
{ {
MultiBlock multiBlock = Decoder.DecodeMulti(cpuPreset, memoryManager, address); MultiBlock multiBlock = Decoder.DecodeMulti(cpuPreset, memoryManager, address);
@@ -215,7 +215,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
RegisterAllocator regAlloc, RegisterAllocator regAlloc,
TailMerger tailMerger, TailMerger tailMerger,
Action writeEpilogue, Action writeEpilogue,
AddressTable<ulong> funcTable, IAddressTable<ulong> funcTable,
nint dispatchStubPtr, nint dispatchStubPtr,
InstName name, InstName name,
ulong pc, ulong pc,
@@ -299,7 +299,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
RegisterAllocator regAlloc, RegisterAllocator regAlloc,
TailMerger tailMerger, TailMerger tailMerger,
Action writeEpilogue, Action writeEpilogue,
AddressTable<ulong> funcTable, IAddressTable<ulong> funcTable,
nint funcPtr, nint funcPtr,
int spillBaseOffset, int spillBaseOffset,
ulong pc, ulong pc,
@@ -310,7 +310,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
int tempGuestAddress = -1; int tempGuestAddress = -1;
bool inlineLookup = guestAddress.Kind != OperandKind.Constant && bool inlineLookup = guestAddress.Kind != OperandKind.Constant &&
funcTable is { Sparse: true }; funcTable.TableType == AddressTableType.Sparse;
if (guestAddress.Kind == OperandKind.Constant) if (guestAddress.Kind == OperandKind.Constant)
{ {
@@ -106,7 +106,9 @@ namespace Ryujinx.Cpu.LightningJit.Cache
{ {
int endOffs = offset + size; int endOffs = offset + size;
int regionStart = (offset % (int)CacheSize) & ~_pageMask; int regionStart = (offset % (int)CacheSize) & ~_pageMask;
int regionEnd = ((endOffs % (int)CacheSize) + _pageMask) & ~_pageMask; int regionEnd = endOffs % (int)CacheSize == 0
? (((int)CacheSize) + _pageMask) & ~_pageMask
: ((endOffs % (int)CacheSize) + _pageMask) & ~_pageMask;
GetRegion(offset).Block.MapAsRwx((ulong)regionStart, (ulong)(regionEnd - regionStart)); GetRegion(offset).Block.MapAsRwx((ulong)regionStart, (ulong)(regionEnd - regionStart));
} }
@@ -115,7 +117,9 @@ namespace Ryujinx.Cpu.LightningJit.Cache
{ {
int endOffs = offset + size; int endOffs = offset + size;
int regionStart = (offset % (int)CacheSize) & ~_pageMask; int regionStart = (offset % (int)CacheSize) & ~_pageMask;
int regionEnd = ((endOffs % (int)CacheSize) + _pageMask) & ~_pageMask; int regionEnd = endOffs % (int)CacheSize == 0
? (((int)CacheSize) + _pageMask) & ~_pageMask
: ((endOffs % (int)CacheSize) + _pageMask) & ~_pageMask;
GetRegion(offset).Block.MapAsRx((ulong)regionStart, (ulong)(regionEnd - regionStart)); GetRegion(offset).Block.MapAsRx((ulong)regionStart, (ulong)(regionEnd - regionStart));
} }
@@ -9,15 +9,15 @@ namespace Ryujinx.Cpu.LightningJit
{ {
private readonly ITickSource _tickSource; private readonly ITickSource _tickSource;
private readonly Translator _translator; private readonly Translator _translator;
private readonly AddressTable<ulong> _functionTable;
public LightningJitCpuContext(ITickSource tickSource, IMemoryManager memory, bool for64Bit) public LightningJitCpuContext(ITickSource tickSource, IMemoryManager memory, bool for64Bit)
{ {
_tickSource = tickSource; _tickSource = tickSource;
_functionTable = AddressTable<ulong>.CreateForArm(for64Bit, memory.Type); bool sparse = memory.Type is not MemoryManagerType.SoftwareMmu and not MemoryManagerType.SoftwarePageTable;
IAddressTable<ulong> functionTable = sparse ? SparseAddressTable<ulong>.CreateForArm(for64Bit) : AddressTable<ulong>.CreateForArm(for64Bit);
_translator = new Translator(memory, _functionTable);
_translator = new Translator(memory, functionTable);
memory.UnmapEvent += UnmapHandler; memory.UnmapEvent += UnmapHandler;
} }
@@ -54,7 +54,7 @@ namespace Ryujinx.Cpu.LightningJit
/// <inheritdoc/> /// <inheritdoc/>
public void PrepareCodeRange(ulong address, ulong size) public void PrepareCodeRange(ulong address, ulong size)
{ {
_functionTable.SignalCodeRange(address, size); _translator.FunctionTable.SignalCodeRange(address, size);
} }
public void Dispose() public void Dispose()
+3 -2
View File
@@ -24,11 +24,11 @@ namespace Ryujinx.Cpu.LightningJit
private bool _disposed; private bool _disposed;
internal TranslatorCache<TranslatedFunction> Functions { get; } internal TranslatorCache<TranslatedFunction> Functions { get; }
internal AddressTable<ulong> FunctionTable { get; } internal IAddressTable<ulong> FunctionTable { get; }
internal TranslatorStubs Stubs { get; } internal TranslatorStubs Stubs { get; }
internal IMemoryManager Memory { get; } internal IMemoryManager Memory { get; }
public Translator(IMemoryManager memory, AddressTable<ulong> functionTable) public Translator(IMemoryManager memory, IAddressTable<ulong> functionTable)
{ {
Memory = memory; Memory = memory;
@@ -45,6 +45,7 @@ namespace Ryujinx.Cpu.LightningJit
Functions = new TranslatorCache<TranslatedFunction>(); Functions = new TranslatorCache<TranslatedFunction>();
FunctionTable = functionTable; FunctionTable = functionTable;
Stubs = new TranslatorStubs(_jitCache, FunctionTable, _noWxCache); Stubs = new TranslatorStubs(_jitCache, FunctionTable, _noWxCache);
FunctionTable.Fill = (ulong)Stubs.SlowDispatchStub; FunctionTable.Fill = (ulong)Stubs.SlowDispatchStub;
+247
View File
@@ -0,0 +1,247 @@
using ARMeilleure.Common;
using Ryujinx.Common;
using Ryujinx.Cpu.Signal;
using Ryujinx.Memory;
using System;
using System.Linq;
using System.Runtime.InteropServices;
using static Ryujinx.Cpu.MemoryEhMeilleure;
namespace Ryujinx.Cpu
{
/// <summary>
/// Represents a table of guest address to a value.
/// </summary>
/// <typeparam name="TEntry">Type of the value</typeparam>
public unsafe class SparseAddressTable<TEntry> : IAddressTable<TEntry> where TEntry : unmanaged
{
/// <summary>
/// A sparsely mapped block of memory with a signal handler to map pages as they're accessed.
/// </summary>
private readonly struct TableSparseBlock : IDisposable
{
public readonly SparseMemoryBlock Block;
private readonly TrackingEventDelegate _trackingEvent;
public TableSparseBlock(ulong size, Action<nint> ensureMapped, PageInitDelegate pageInit, MemoryBlock fill)
{
SparseMemoryBlock block = new(size, pageInit, fill);
_trackingEvent = (address, _, _) =>
{
ulong pointer = (ulong)block.Block.Pointer + address;
ensureMapped((nint)pointer);
return pointer;
};
bool added = NativeSignalHandler.AddTrackedRegion(
(nuint)block.Block.Pointer,
(nuint)(block.Block.Pointer + (nint)block.Block.Size),
Marshal.GetFunctionPointerForDelegate(_trackingEvent));
if (!added)
{
throw new InvalidOperationException("Number of allowed tracked regions exceeded.");
}
Block = block;
}
public void Dispose()
{
NativeSignalHandler.RemoveTrackedRegion((nuint)Block.Block.Pointer);
Block.Dispose();
}
}
private bool _disposed;
private TEntry* _table;
private TEntry _fill;
private readonly TableSparseBlock _block;
private readonly MemoryBlock _fillBlock;
/// <inheritdoc/>
public AddressTableType TableType => AddressTableType.Sparse;
/// <inheritdoc/>
public ulong Mask { get; }
/// <inheritdoc/>
public AddressTableLevel[] Levels { get; }
/// <inheritdoc/>
public TEntry Fill
{
get
{
return _fill;
}
set
{
UpdateFill(value);
}
}
/// <inheritdoc/>
public nint Base
{
get
{
ObjectDisposedException.ThrowIf(_disposed, this);
return (nint)_table;
}
}
/// <summary>
/// Constructs a new instance of the <see cref="AddressTable{TEntry}"/> class with the specified list of
/// <see cref="AddressTableLevel"/>.
/// </summary>
/// <param name="levels">Levels for the address table</param>
/// <exception cref="ArgumentNullException"><paramref name="levels"/> is null</exception>
/// <exception cref="ArgumentException">Length of <paramref name="levels"/> is less than 2</exception>
public SparseAddressTable(AddressTableLevel[] levels)
{
ArgumentNullException.ThrowIfNull(levels);
Levels = levels;
Mask = 0;
foreach (AddressTableLevel level in Levels)
{
Mask |= level.Mask;
}
_fillBlock = new MemoryBlock(MemoryBlock.GetPageSize() << 10, MemoryAllocationFlags.Mirrorable);
// We need to use the full size, as some games dynamically expand the code range (e.g. SSBU)
// Limiting the size to only the requested code range will cause crashes
// This should not be an issue tho, as the SparseBlock dynamically allocates memory as needed, and
// Falls back to the fill block in case guest code tries to call an unmapped function.
ulong bottomLevelSize = (1ul << Levels.Last().Length) * (ulong)sizeof(TEntry);
_block = new TableSparseBlock(bottomLevelSize, EnsureMapped, InitLeafPage, _fillBlock);
_table = (TEntry*)_block.Block.Block.Pointer;
}
/// <summary>
/// Create an <see cref="AddressTable{TEntry}"/> instance for an ARM function table.
/// Selects the best table structure for A32/A64, taking into account the selected memory manager type.
/// </summary>
/// <param name="for64Bits">True if the guest is A64, false otherwise</param>
/// <returns>An <see cref="AddressTable{TEntry}"/> for ARM function lookup</returns>
public static SparseAddressTable<TEntry> CreateForArm(bool for64Bits)
{
return new SparseAddressTable<TEntry>(AddressTablePresets.GetArmPreset(for64Bits, true));
}
/// <summary>
/// Update the fill value for the bottom level of the table.
/// </summary>
/// <param name="fillValue">New fill value</param>
private void UpdateFill(TEntry fillValue)
{
if (_fillBlock != null)
{
Span<byte> span = _fillBlock.GetSpan(0, (int)_fillBlock.Size);
MemoryMarshal.Cast<byte, TEntry>(span).Fill(fillValue);
}
_fill = fillValue;
}
/// <summary>
/// Signal that the given code range exists.
/// </summary>
/// <param name="address"></param>
/// <param name="size"></param>
public void SignalCodeRange(ulong address, ulong size)
{
}
/// <inheritdoc/>
public bool IsValid(ulong address)
{
return (address & ~Mask) == 0;
}
/// <inheritdoc/>
public ref TEntry GetValue(ulong address)
{
ObjectDisposedException.ThrowIf(_disposed, this);
if (!IsValid(address))
{
throw new ArgumentException($"Address 0x{address:X} is not mapped onto the table.", nameof(address));
}
long index = Levels.Last().GetValue(address);
EnsureMapped((nint)(_table + index));
return ref _table[index];
}
/// <summary>
/// Ensure the given pointer is mapped in any overlapping sparse reservations.
/// </summary>
/// <param name="ptr">Pointer to be mapped</param>
private void EnsureMapped(nint ptr)
{
SparseMemoryBlock sparse = _block.Block;
if (ptr >= sparse.Block.Pointer && ptr < sparse.Block.Pointer + (nint)sparse.Block.Size)
{
sparse.EnsureMapped((ulong)(ptr - sparse.Block.Pointer));
}
}
/// <summary>
/// Initialize a leaf page with the fill value.
/// </summary>
/// <param name="page">Page to initialize</param>
private void InitLeafPage(Span<byte> page)
{
MemoryMarshal.Cast<byte, TEntry>(page).Fill(_fill);
}
/// <summary>
/// Releases all resources used by the <see cref="AddressTable{TEntry}"/> instance.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Releases all unmanaged and optionally managed resources used by the <see cref="AddressTable{TEntry}"/>
/// instance.
/// </summary>
/// <param name="disposing"><see langword="true"/> to dispose managed resources also; otherwise just unmanaged resouces</param>
protected virtual void Dispose(bool disposing)
{
if (_disposed)
{
return;
}
_block.Dispose();
_fillBlock.Dispose();
_disposed = true;
}
/// <summary>
/// Frees resources used by the <see cref="AddressTable{TEntry}"/> instance.
/// </summary>
~SparseAddressTable()
{
Dispose(false);
}
}
}
@@ -195,11 +195,14 @@ namespace Ryujinx.Graphics.GAL.Multithreading
{ {
// The reference table is sized so that it will never overflow, so long as the references are taken after the command is allocated. // The reference table is sized so that it will never overflow, so long as the references are taken after the command is allocated.
int index = _refProducerPtr; // make sure increment is thread safe
int index = Interlocked.Increment(ref _refProducerPtr) - 1;
index %= _refQueue.Length;
_refQueue[index] = obj; _refQueue[index] = obj;
_refProducerPtr = (_refProducerPtr + 1) % _refQueue.Length; _refProducerPtr %= _refQueue.Length;
return index; return index;
} }
@@ -531,10 +534,15 @@ namespace Ryujinx.Graphics.GAL.Multithreading
_running = false; _running = false;
_galWorkAvailable.Set(); _galWorkAvailable.Set();
if (_gpuThread != null && _gpuThread.IsAlive) if (_gpuThread is { IsAlive: true })
{ {
_gpuThread.Join(); _gpuThread.Join();
} }
if (_backendThread is { IsAlive: true })
{
_backendThread.Join();
}
// Dispose the renderer. // Dispose the renderer.
_baseRenderer.Dispose(); _baseRenderer.Dispose();
+1 -1
View File
@@ -31,7 +31,7 @@
<PackageReference Include="SkiaSharp.NativeAssets.macOS" /> <PackageReference Include="SkiaSharp.NativeAssets.macOS" />
<PackageReference Include="SkiaSharp.NativeAssets.Linux.NoDependencies" /> <PackageReference Include="SkiaSharp.NativeAssets.Linux.NoDependencies" />
<PackageReference Include="NetCoreServer" /> <PackageReference Include="NetCoreServer" />
<PackageReference Include="Open.NAT.Core" /> <PackageReference Include="Open.Nat.V2" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
+17 -9
View File
@@ -21,6 +21,7 @@ namespace Ryujinx.Memory
private ulong _mappedBlockUsage; private ulong _mappedBlockUsage;
private readonly ulong[] _mappedPageBitmap; private readonly ulong[] _mappedPageBitmap;
private readonly MemoryBlock _fill;
public MemoryBlock Block => _reservedBlock; public MemoryBlock Block => _reservedBlock;
@@ -34,23 +35,24 @@ namespace Ryujinx.Memory
int pages = (int)BitUtils.DivRoundUp(size, _pageSize); int pages = (int)BitUtils.DivRoundUp(size, _pageSize);
int bitmapEntries = BitUtils.DivRoundUp(pages, 64); int bitmapEntries = BitUtils.DivRoundUp(pages, 64);
_mappedPageBitmap = new ulong[bitmapEntries]; _mappedPageBitmap = new ulong[bitmapEntries];
_fill = fill;
if (fill != null) if (_fill is not null)
{ {
if (_fill.Size % _pageSize !=0)
{
throw new ArgumentException("Fill memory block should be page sized.", nameof(_fill));
}
// Fill the block with mappings from the fill block. // Fill the block with mappings from the fill block.
if (fill.Size % _pageSize != 0) int repeats = (int)BitUtils.DivRoundUp(size, _fill.Size);
{
throw new ArgumentException("Fill memory block should be page aligned.", nameof(fill));
}
int repeats = (int)BitUtils.DivRoundUp(size, fill.Size);
ulong offset = 0; ulong offset = 0;
for (int i = 0; i < repeats; i++) for (int i = 0; i < repeats; i++)
{ {
_reservedBlock.MapView(fill, 0, offset, Math.Min(fill.Size, size - offset)); _reservedBlock.MapView(_fill, 0, offset, Math.Min(_fill.Size, size - offset));
offset += fill.Size; offset += _fill.Size;
} }
} }
@@ -75,6 +77,12 @@ namespace Ryujinx.Memory
} }
_pageInit(block.GetSpan(_mappedBlockUsage, (int)_pageSize)); _pageInit(block.GetSpan(_mappedBlockUsage, (int)_pageSize));
if (_fill is not null)
{
_reservedBlock.UnmapView(_fill, pageOffset, _pageSize);
}
_reservedBlock.MapView(block, _mappedBlockUsage, pageOffset, _pageSize); _reservedBlock.MapView(block, _mappedBlockUsage, pageOffset, _pageSize);
_mappedBlockUsage += _pageSize; _mappedBlockUsage += _pageSize;
+38 -1
View File
@@ -4,6 +4,7 @@ using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Net.Http;
using System.Threading; using System.Threading;
using SDL; using SDL;
using static SDL.SDL3; using static SDL.SDL3;
@@ -43,6 +44,9 @@ namespace Ryujinx.SDL3.Common
private SDL3Driver() { } private SDL3Driver() { }
private static readonly HttpClient _httpClient = new();
private const string GamepadDbUrl = "https://raw.githubusercontent.com/mdqinc/SDL_GameControllerDB/refs/heads/master/gamecontrollerdb.txt";
public void Initialize() public void Initialize()
{ {
lock (_lock) lock (_lock)
@@ -96,8 +100,10 @@ namespace Ryujinx.SDL3.Common
SDL_SetEventEnabled((uint)SDL_EventType.SDL_EVENT_GAMEPAD_SENSOR_UPDATE, false); SDL_SetEventEnabled((uint)SDL_EventType.SDL_EVENT_GAMEPAD_SENSOR_UPDATE, false);
string gamepadDbPath = Path.Combine(AppDataManager.BaseDirPath, "SDL_GameControllerDB.txt"); string gamepadDbPath = Path.Combine(AppDataManager.BaseDirPath, "gamecontrollerdb.txt");
UpdateGamepadDb(gamepadDbPath);
if (File.Exists(gamepadDbPath)) if (File.Exists(gamepadDbPath))
{ {
SDL_AddGamepadMappingsFromFile(gamepadDbPath); SDL_AddGamepadMappingsFromFile(gamepadDbPath);
@@ -110,6 +116,37 @@ namespace Ryujinx.SDL3.Common
} }
} }
private static void UpdateGamepadDb(string gamepadDbPath)
{
try
{
byte[] remoteBytes = _httpClient.GetByteArrayAsync(GamepadDbUrl).GetAwaiter().GetResult();
bool shouldWrite = true;
if (File.Exists(gamepadDbPath))
{
byte[] localBytes = File.ReadAllBytes(gamepadDbPath);
if (localBytes.AsSpan().SequenceEqual(remoteBytes))
{
shouldWrite = false;
}
}
if (shouldWrite)
{
File.WriteAllBytes(gamepadDbPath ?? "", remoteBytes);
Logger.Info?.Print(LogClass.Application, "Updated gamepad database.");
}
}
catch (Exception ex)
{
Logger.Warning?.Print(LogClass.Application, $"Failed to check/download gamepad database, using existing local copy if present: {ex.Message}");
}
}
public bool RegisterWindow(SDL_WindowID windowId, Action<SDL_Event> windowEventHandler) public bool RegisterWindow(SDL_WindowID windowId, Action<SDL_Event> windowEventHandler)
{ {
return _registeredWindowHandlers.TryAdd(windowId, windowEventHandler); return _registeredWindowHandlers.TryAdd(windowId, windowEventHandler);
+4 -1
View File
@@ -13,7 +13,10 @@ namespace Ryujinx.Tests.Cpu
public CpuContext(IMemoryManager memory, bool for64Bit) public CpuContext(IMemoryManager memory, bool for64Bit)
{ {
_translator = new Translator(new JitMemoryAllocator(), memory, AddressTable<ulong>.CreateForArm(for64Bit, memory.Type)); bool sparse = memory.Type is not MemoryManagerType.SoftwareMmu and not MemoryManagerType.SoftwarePageTable;
IAddressTable<ulong> functionTable = sparse ? SparseAddressTable<ulong>.CreateForArm(for64Bit) : AddressTable<ulong>.CreateForArm(for64Bit);
_translator = new Translator(new JitMemoryAllocator(), memory, functionTable);
memory.UnmapEvent += UnmapHandler; memory.UnmapEvent += UnmapHandler;
} }
+2 -2
View File
@@ -1,7 +1,7 @@
using ARMeilleure.Common; using ARMeilleure.Common;
using ARMeilleure.Memory;
using ARMeilleure.Translation; using ARMeilleure.Translation;
using NUnit.Framework; using NUnit.Framework;
using Ryujinx.Cpu;
using Ryujinx.Cpu.Jit; using Ryujinx.Cpu.Jit;
using Ryujinx.Tests.Memory; using Ryujinx.Tests.Memory;
using System; using System;
@@ -20,7 +20,7 @@ namespace Ryujinx.Tests.Cpu
_translator ??= new Translator( _translator ??= new Translator(
new JitMemoryAllocator(), new JitMemoryAllocator(),
new MockMemoryManager(), new MockMemoryManager(),
AddressTable<ulong>.CreateForArm(true, MemoryManagerType.SoftwarePageTable)); AddressTable<ulong>.CreateForArm(true));
} }
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
+1 -1
View File
@@ -60,7 +60,7 @@ namespace Ryujinx.Tests.Memory
_translator ??= new Translator( _translator ??= new Translator(
new JitMemoryAllocator(), new JitMemoryAllocator(),
new MockMemoryManager(), new MockMemoryManager(),
AddressTable<ulong>.CreateForArm(true, MemoryManagerType.SoftwarePageTable)); AddressTable<ulong>.CreateForArm(true));
NativeSignalHandler.InitializeSignalHandler(); NativeSignalHandler.InitializeSignalHandler();
} }
@@ -1,5 +1,4 @@
using Humanizer; using Humanizer;
using Humanizer.Localisation;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using System; using System;
using System.Globalization; using System.Globalization;