Compare commits

..

4 Commits

Author SHA1 Message Date
Jacobwasbeast
965fb9dd5f Implement Surface Flinger shared layers.
Co-authored-by: Alula <6276139+alula@users.noreply.github.com>
2025-02-07 04:27:01 -06:00
Jacobwasbeast
e2a5e69f4c Implement IHid fix from alula/qlaunch
Co-authored-by: Alula <6276139+alula@users.noreply.github.com>
2025-02-07 04:24:37 -06:00
Jacobwasbeast
7f27b791f8 Refactor Share Buffer Implementation to Follow Code Style Guidelines 2025-02-07 04:16:27 -06:00
Jacobwasbeast
3ca8618f5f Implement changes from gdkchan/buffer-sharing-rebased
Co-authored-by: gdkchan <gab.dark.100@gmail.com>
Co-authored-by: Alula <6276139+alula@users.noreply.github.com>
2025-02-06 03:48:54 -06:00
462 changed files with 23599 additions and 20018 deletions

View File

@@ -29,7 +29,7 @@ env:
jobs:
tag:
name: Create tag
runs-on: ubuntu-24.04
runs-on: ubuntu-20.04
steps:
- name: Get version info
id: version_info
@@ -202,7 +202,7 @@ jobs:
macos_release:
name: Release MacOS universal
runs-on: ubuntu-24.04
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v4

View File

@@ -18,7 +18,7 @@ env:
jobs:
tag:
name: Create tag
runs-on: ubuntu-24.04
runs-on: ubuntu-20.04
steps:
- name: Get version info
id: version_info
@@ -183,7 +183,7 @@ jobs:
macos_release:
name: Release MacOS universal
runs-on: ubuntu-24.04
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v4

View File

@@ -42,10 +42,11 @@
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.3-build14" />
<PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" />
<PackageVersion Include="Ryujinx.SDL2-CS" Version="2.30.0-build32" />
<PackageVersion Include="Gommon" Version="2.7.1.1" />
<PackageVersion Include="Gommon" Version="2.7.1" />
<PackageVersion Include="securifybv.ShellLink" Version="0.1.0" />
<PackageVersion Include="Sep" Version="0.6.0" />
<PackageVersion Include="shaderc.net" Version="0.1.0" />
<PackageVersion Include="SharpMetal" Version="1.0.0-preview21" />
<PackageVersion Include="SharpZipLib" Version="1.4.2" />
<PackageVersion Include="Silk.NET.Vulkan" Version="2.22.0" />
<PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.22.0" />

View File

@@ -1,12 +1,3 @@
# You have stumbled on a gimped version of Ryujinx.
This is the NativeAOT branch. It should run much faster than the normal version, however it's ruined by one thing: the Macro JIT.
This single part of Ryujinx, which handles common Maxwell GPU Macros by using .NET Reflection Emit, is entirely unusable via NativeAOT and it kills the performance.
I was getting 20FPS in BOTW with 98% FIFO usage.
This is compared to over 100FPS with about 70% FIFO usage in standard, managed C#, Ryujinx.
If there are any JIT wizards out there; I am calling to you. I believe getting this to work would be a game changer.
[This is the offending JIT implementation.](https://github.com/Ryubing/Ryujinx/blob/feature/native_aot/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroJitCompiler.cs)
<table align="center">
<tr>
<td align="center" width="25%">
@@ -48,12 +39,12 @@ If there are any JIT wizards out there; I am calling to you. I believe getting t
<p align="center">
Click below to join the Discord:
<br>
<a href="https://discord.gg/PEuzjrFXUA">
<a href="https://discord.gg/dHPrkBkkyA">
<img src="https://img.shields.io/discord/1294443224030511104?color=5865F2&label=Ryubing&logo=discord&logoColor=white" alt="Discord">
</a>
<br>
<br>
<img src="https://raw.githubusercontent.com/Ryubing/Ryujinx/refs/heads/master/docs/shell.png">
<img src="https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/docs/shell.png">
</p>
## Usage
@@ -106,7 +97,7 @@ If you are planning to contribute or just want to learn more about this project
- **Input**
We currently have support for keyboard, mouse, touch input, Joy-Con input support, and nearly all controllers.
We currently have support for keyboard, mouse, touch input, JoyCon input support, and nearly all controllers.
Motion controls are natively supported in most cases; for dual-JoyCon motion support, DS4Windows or BetterJoy are currently required.
In all scenarios, you can set up everything inside the input configuration menu.

View File

@@ -77,6 +77,15 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Kernel.Gene
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.HLE.Generators", "src\Ryujinx.HLE.Generators\Ryujinx.HLE.Generators.csproj", "{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.Graphics.Metal", "src\Ryujinx.Graphics.Metal\Ryujinx.Graphics.Metal.csproj", "{C08931FA-1191-417A-864F-3882D93E683B}"
ProjectSection(ProjectDependencies) = postProject
{A602AE97-91A5-4608-8DF1-EBF4ED7A0B9E} = {A602AE97-91A5-4608-8DF1-EBF4ED7A0B9E}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.BuildValidationTasks", "src\Ryujinx.BuildValidationTasks\Ryujinx.BuildValidationTasks.csproj", "{4A89A234-4F19-497D-A576-DDE8CDFC5B22}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.Graphics.Metal.SharpMetalExtensions", "src/Ryujinx.Graphics.Metal.SharpMetalExtensions\Ryujinx.Graphics.Metal.SharpMetalExtensions.csproj", "{81EA598C-DBA1-40B0-8DA4-4796B78F2037}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{36F870C1-3E5F-485F-B426-F0645AF78751}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
@@ -86,327 +95,170 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
.github\workflows\release.yml = .github\workflows\release.yml
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.BuildValidationTasks", "src\Ryujinx.BuildValidationTasks\Ryujinx.BuildValidationTasks.csproj", "{4A89A234-4F19-497D-A576-DDE8CDFC5B22}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
DebugAOT|Any CPU = DebugAOT|Any CPU
Release|Any CPU = Release|Any CPU
ReleaseAOT|Any CPU = ReleaseAOT|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{EBB55AEA-C7D7-4DEB-BF96-FA1789E225E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EBB55AEA-C7D7-4DEB-BF96-FA1789E225E9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EBB55AEA-C7D7-4DEB-BF96-FA1789E225E9}.DebugAOT|Any CPU.ActiveCfg = DebugAOT|Any CPU
{EBB55AEA-C7D7-4DEB-BF96-FA1789E225E9}.DebugAOT|Any CPU.Build.0 = DebugAOT|Any CPU
{EBB55AEA-C7D7-4DEB-BF96-FA1789E225E9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EBB55AEA-C7D7-4DEB-BF96-FA1789E225E9}.Release|Any CPU.Build.0 = Release|Any CPU
{EBB55AEA-C7D7-4DEB-BF96-FA1789E225E9}.ReleaseAOT|Any CPU.ActiveCfg = ReleaseAOT|Any CPU
{EBB55AEA-C7D7-4DEB-BF96-FA1789E225E9}.ReleaseAOT|Any CPU.Build.0 = ReleaseAOT|Any CPU
{D8F72938-78EF-4E8C-BAFE-531C9C3C8F15}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D8F72938-78EF-4E8C-BAFE-531C9C3C8F15}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D8F72938-78EF-4E8C-BAFE-531C9C3C8F15}.DebugAOT|Any CPU.ActiveCfg = DebugAOT|Any CPU
{D8F72938-78EF-4E8C-BAFE-531C9C3C8F15}.DebugAOT|Any CPU.Build.0 = DebugAOT|Any CPU
{D8F72938-78EF-4E8C-BAFE-531C9C3C8F15}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D8F72938-78EF-4E8C-BAFE-531C9C3C8F15}.Release|Any CPU.Build.0 = Release|Any CPU
{D8F72938-78EF-4E8C-BAFE-531C9C3C8F15}.ReleaseAOT|Any CPU.ActiveCfg = ReleaseAOT|Any CPU
{D8F72938-78EF-4E8C-BAFE-531C9C3C8F15}.ReleaseAOT|Any CPU.Build.0 = ReleaseAOT|Any CPU
{CB92CFF9-1D62-4D4F-9E88-8130EF61E351}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CB92CFF9-1D62-4D4F-9E88-8130EF61E351}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CB92CFF9-1D62-4D4F-9E88-8130EF61E351}.DebugAOT|Any CPU.ActiveCfg = DebugAOT|Any CPU
{CB92CFF9-1D62-4D4F-9E88-8130EF61E351}.DebugAOT|Any CPU.Build.0 = DebugAOT|Any CPU
{CB92CFF9-1D62-4D4F-9E88-8130EF61E351}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CB92CFF9-1D62-4D4F-9E88-8130EF61E351}.Release|Any CPU.Build.0 = Release|Any CPU
{CB92CFF9-1D62-4D4F-9E88-8130EF61E351}.ReleaseAOT|Any CPU.ActiveCfg = ReleaseAOT|Any CPU
{CB92CFF9-1D62-4D4F-9E88-8130EF61E351}.ReleaseAOT|Any CPU.Build.0 = ReleaseAOT|Any CPU
{3AB294D0-2230-468F-9EB3-BDFCAEAE99A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3AB294D0-2230-468F-9EB3-BDFCAEAE99A5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3AB294D0-2230-468F-9EB3-BDFCAEAE99A5}.DebugAOT|Any CPU.ActiveCfg = DebugAOT|Any CPU
{3AB294D0-2230-468F-9EB3-BDFCAEAE99A5}.DebugAOT|Any CPU.Build.0 = DebugAOT|Any CPU
{3AB294D0-2230-468F-9EB3-BDFCAEAE99A5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3AB294D0-2230-468F-9EB3-BDFCAEAE99A5}.Release|Any CPU.Build.0 = Release|Any CPU
{3AB294D0-2230-468F-9EB3-BDFCAEAE99A5}.ReleaseAOT|Any CPU.ActiveCfg = ReleaseAOT|Any CPU
{3AB294D0-2230-468F-9EB3-BDFCAEAE99A5}.ReleaseAOT|Any CPU.Build.0 = ReleaseAOT|Any CPU
{5FD4E4F6-8928-4B3C-BE07-28A675C17226}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5FD4E4F6-8928-4B3C-BE07-28A675C17226}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5FD4E4F6-8928-4B3C-BE07-28A675C17226}.DebugAOT|Any CPU.ActiveCfg = DebugAOT|Any CPU
{5FD4E4F6-8928-4B3C-BE07-28A675C17226}.DebugAOT|Any CPU.Build.0 = DebugAOT|Any CPU
{5FD4E4F6-8928-4B3C-BE07-28A675C17226}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5FD4E4F6-8928-4B3C-BE07-28A675C17226}.Release|Any CPU.Build.0 = Release|Any CPU
{5FD4E4F6-8928-4B3C-BE07-28A675C17226}.ReleaseAOT|Any CPU.ActiveCfg = ReleaseAOT|Any CPU
{5FD4E4F6-8928-4B3C-BE07-28A675C17226}.ReleaseAOT|Any CPU.Build.0 = ReleaseAOT|Any CPU
{ABF09A5E-2D8B-4B6F-A51D-5CE414DDB15A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{ABF09A5E-2D8B-4B6F-A51D-5CE414DDB15A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{ABF09A5E-2D8B-4B6F-A51D-5CE414DDB15A}.DebugAOT|Any CPU.ActiveCfg = DebugAOT|Any CPU
{ABF09A5E-2D8B-4B6F-A51D-5CE414DDB15A}.DebugAOT|Any CPU.Build.0 = DebugAOT|Any CPU
{ABF09A5E-2D8B-4B6F-A51D-5CE414DDB15A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{ABF09A5E-2D8B-4B6F-A51D-5CE414DDB15A}.Release|Any CPU.Build.0 = Release|Any CPU
{ABF09A5E-2D8B-4B6F-A51D-5CE414DDB15A}.ReleaseAOT|Any CPU.ActiveCfg = ReleaseAOT|Any CPU
{ABF09A5E-2D8B-4B6F-A51D-5CE414DDB15A}.ReleaseAOT|Any CPU.Build.0 = ReleaseAOT|Any CPU
{ADA7EA87-0D63-4D97-9433-922A2124401F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{ADA7EA87-0D63-4D97-9433-922A2124401F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{ADA7EA87-0D63-4D97-9433-922A2124401F}.DebugAOT|Any CPU.ActiveCfg = DebugAOT|Any CPU
{ADA7EA87-0D63-4D97-9433-922A2124401F}.DebugAOT|Any CPU.Build.0 = DebugAOT|Any CPU
{ADA7EA87-0D63-4D97-9433-922A2124401F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{ADA7EA87-0D63-4D97-9433-922A2124401F}.Release|Any CPU.Build.0 = Release|Any CPU
{ADA7EA87-0D63-4D97-9433-922A2124401F}.ReleaseAOT|Any CPU.ActiveCfg = ReleaseAOT|Any CPU
{ADA7EA87-0D63-4D97-9433-922A2124401F}.ReleaseAOT|Any CPU.Build.0 = ReleaseAOT|Any CPU
{A602AE97-91A5-4608-8DF1-EBF4ED7A0B9E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A602AE97-91A5-4608-8DF1-EBF4ED7A0B9E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A602AE97-91A5-4608-8DF1-EBF4ED7A0B9E}.DebugAOT|Any CPU.ActiveCfg = DebugAOT|Any CPU
{A602AE97-91A5-4608-8DF1-EBF4ED7A0B9E}.DebugAOT|Any CPU.Build.0 = DebugAOT|Any CPU
{A602AE97-91A5-4608-8DF1-EBF4ED7A0B9E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A602AE97-91A5-4608-8DF1-EBF4ED7A0B9E}.Release|Any CPU.Build.0 = Release|Any CPU
{A602AE97-91A5-4608-8DF1-EBF4ED7A0B9E}.ReleaseAOT|Any CPU.ActiveCfg = ReleaseAOT|Any CPU
{A602AE97-91A5-4608-8DF1-EBF4ED7A0B9E}.ReleaseAOT|Any CPU.Build.0 = ReleaseAOT|Any CPU
{9558FB96-075D-4219-8FFF-401979DC0B69}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9558FB96-075D-4219-8FFF-401979DC0B69}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9558FB96-075D-4219-8FFF-401979DC0B69}.DebugAOT|Any CPU.ActiveCfg = DebugAOT|Any CPU
{9558FB96-075D-4219-8FFF-401979DC0B69}.DebugAOT|Any CPU.Build.0 = DebugAOT|Any CPU
{9558FB96-075D-4219-8FFF-401979DC0B69}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9558FB96-075D-4219-8FFF-401979DC0B69}.Release|Any CPU.Build.0 = Release|Any CPU
{9558FB96-075D-4219-8FFF-401979DC0B69}.ReleaseAOT|Any CPU.ActiveCfg = ReleaseAOT|Any CPU
{9558FB96-075D-4219-8FFF-401979DC0B69}.ReleaseAOT|Any CPU.Build.0 = ReleaseAOT|Any CPU
{E1B1AD28-289D-47B7-A106-326972240207}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E1B1AD28-289D-47B7-A106-326972240207}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E1B1AD28-289D-47B7-A106-326972240207}.DebugAOT|Any CPU.ActiveCfg = DebugAOT|Any CPU
{E1B1AD28-289D-47B7-A106-326972240207}.DebugAOT|Any CPU.Build.0 = DebugAOT|Any CPU
{E1B1AD28-289D-47B7-A106-326972240207}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E1B1AD28-289D-47B7-A106-326972240207}.Release|Any CPU.Build.0 = Release|Any CPU
{E1B1AD28-289D-47B7-A106-326972240207}.ReleaseAOT|Any CPU.ActiveCfg = ReleaseAOT|Any CPU
{E1B1AD28-289D-47B7-A106-326972240207}.ReleaseAOT|Any CPU.Build.0 = ReleaseAOT|Any CPU
{03B955CD-AD84-4B93-AAA7-BF17923BBAA5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{03B955CD-AD84-4B93-AAA7-BF17923BBAA5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{03B955CD-AD84-4B93-AAA7-BF17923BBAA5}.DebugAOT|Any CPU.ActiveCfg = DebugAOT|Any CPU
{03B955CD-AD84-4B93-AAA7-BF17923BBAA5}.DebugAOT|Any CPU.Build.0 = DebugAOT|Any CPU
{03B955CD-AD84-4B93-AAA7-BF17923BBAA5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{03B955CD-AD84-4B93-AAA7-BF17923BBAA5}.Release|Any CPU.Build.0 = Release|Any CPU
{03B955CD-AD84-4B93-AAA7-BF17923BBAA5}.ReleaseAOT|Any CPU.ActiveCfg = ReleaseAOT|Any CPU
{03B955CD-AD84-4B93-AAA7-BF17923BBAA5}.ReleaseAOT|Any CPU.Build.0 = ReleaseAOT|Any CPU
{85A0FA56-DC01-4A42-8808-70DAC76BD66D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{85A0FA56-DC01-4A42-8808-70DAC76BD66D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{85A0FA56-DC01-4A42-8808-70DAC76BD66D}.DebugAOT|Any CPU.ActiveCfg = DebugAOT|Any CPU
{85A0FA56-DC01-4A42-8808-70DAC76BD66D}.DebugAOT|Any CPU.Build.0 = DebugAOT|Any CPU
{85A0FA56-DC01-4A42-8808-70DAC76BD66D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{85A0FA56-DC01-4A42-8808-70DAC76BD66D}.Release|Any CPU.Build.0 = Release|Any CPU
{85A0FA56-DC01-4A42-8808-70DAC76BD66D}.ReleaseAOT|Any CPU.ActiveCfg = ReleaseAOT|Any CPU
{85A0FA56-DC01-4A42-8808-70DAC76BD66D}.ReleaseAOT|Any CPU.Build.0 = ReleaseAOT|Any CPU
{806ACF6D-90B0-45D0-A1AC-5F220F3B3985}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{806ACF6D-90B0-45D0-A1AC-5F220F3B3985}.Debug|Any CPU.Build.0 = Debug|Any CPU
{806ACF6D-90B0-45D0-A1AC-5F220F3B3985}.DebugAOT|Any CPU.ActiveCfg = DebugAOT|Any CPU
{806ACF6D-90B0-45D0-A1AC-5F220F3B3985}.DebugAOT|Any CPU.Build.0 = DebugAOT|Any CPU
{806ACF6D-90B0-45D0-A1AC-5F220F3B3985}.Release|Any CPU.ActiveCfg = Release|Any CPU
{806ACF6D-90B0-45D0-A1AC-5F220F3B3985}.Release|Any CPU.Build.0 = Release|Any CPU
{806ACF6D-90B0-45D0-A1AC-5F220F3B3985}.ReleaseAOT|Any CPU.ActiveCfg = ReleaseAOT|Any CPU
{806ACF6D-90B0-45D0-A1AC-5F220F3B3985}.ReleaseAOT|Any CPU.Build.0 = ReleaseAOT|Any CPU
{A5E6C691-9E22-4263-8F40-42F002CE66BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A5E6C691-9E22-4263-8F40-42F002CE66BE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A5E6C691-9E22-4263-8F40-42F002CE66BE}.DebugAOT|Any CPU.ActiveCfg = DebugAOT|Any CPU
{A5E6C691-9E22-4263-8F40-42F002CE66BE}.DebugAOT|Any CPU.Build.0 = DebugAOT|Any CPU
{A5E6C691-9E22-4263-8F40-42F002CE66BE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A5E6C691-9E22-4263-8F40-42F002CE66BE}.Release|Any CPU.Build.0 = Release|Any CPU
{A5E6C691-9E22-4263-8F40-42F002CE66BE}.ReleaseAOT|Any CPU.ActiveCfg = ReleaseAOT|Any CPU
{A5E6C691-9E22-4263-8F40-42F002CE66BE}.ReleaseAOT|Any CPU.Build.0 = ReleaseAOT|Any CPU
{D1CC5322-7325-4F6B-9625-194B30BE1296}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D1CC5322-7325-4F6B-9625-194B30BE1296}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D1CC5322-7325-4F6B-9625-194B30BE1296}.DebugAOT|Any CPU.ActiveCfg = DebugAOT|Any CPU
{D1CC5322-7325-4F6B-9625-194B30BE1296}.DebugAOT|Any CPU.Build.0 = DebugAOT|Any CPU
{D1CC5322-7325-4F6B-9625-194B30BE1296}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D1CC5322-7325-4F6B-9625-194B30BE1296}.Release|Any CPU.Build.0 = Release|Any CPU
{D1CC5322-7325-4F6B-9625-194B30BE1296}.ReleaseAOT|Any CPU.ActiveCfg = ReleaseAOT|Any CPU
{D1CC5322-7325-4F6B-9625-194B30BE1296}.ReleaseAOT|Any CPU.Build.0 = ReleaseAOT|Any CPU
{3DF35E3D-D844-4399-A9A1-A9E923264C17}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3DF35E3D-D844-4399-A9A1-A9E923264C17}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3DF35E3D-D844-4399-A9A1-A9E923264C17}.DebugAOT|Any CPU.ActiveCfg = DebugAOT|Any CPU
{3DF35E3D-D844-4399-A9A1-A9E923264C17}.DebugAOT|Any CPU.Build.0 = DebugAOT|Any CPU
{3DF35E3D-D844-4399-A9A1-A9E923264C17}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3DF35E3D-D844-4399-A9A1-A9E923264C17}.Release|Any CPU.Build.0 = Release|Any CPU
{3DF35E3D-D844-4399-A9A1-A9E923264C17}.ReleaseAOT|Any CPU.ActiveCfg = ReleaseAOT|Any CPU
{3DF35E3D-D844-4399-A9A1-A9E923264C17}.ReleaseAOT|Any CPU.Build.0 = ReleaseAOT|Any CPU
{C3002C3C-7B09-4FE7-894A-372EDA22FC6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C3002C3C-7B09-4FE7-894A-372EDA22FC6E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C3002C3C-7B09-4FE7-894A-372EDA22FC6E}.DebugAOT|Any CPU.ActiveCfg = DebugAOT|Any CPU
{C3002C3C-7B09-4FE7-894A-372EDA22FC6E}.DebugAOT|Any CPU.Build.0 = DebugAOT|Any CPU
{C3002C3C-7B09-4FE7-894A-372EDA22FC6E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C3002C3C-7B09-4FE7-894A-372EDA22FC6E}.Release|Any CPU.Build.0 = Release|Any CPU
{C3002C3C-7B09-4FE7-894A-372EDA22FC6E}.ReleaseAOT|Any CPU.ActiveCfg = ReleaseAOT|Any CPU
{C3002C3C-7B09-4FE7-894A-372EDA22FC6E}.ReleaseAOT|Any CPU.Build.0 = ReleaseAOT|Any CPU
{C35F1536-7DE5-4F9D-9604-B5B4E1561947}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C35F1536-7DE5-4F9D-9604-B5B4E1561947}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C35F1536-7DE5-4F9D-9604-B5B4E1561947}.DebugAOT|Any CPU.ActiveCfg = DebugAOT|Any CPU
{C35F1536-7DE5-4F9D-9604-B5B4E1561947}.DebugAOT|Any CPU.Build.0 = DebugAOT|Any CPU
{C35F1536-7DE5-4F9D-9604-B5B4E1561947}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C35F1536-7DE5-4F9D-9604-B5B4E1561947}.Release|Any CPU.Build.0 = Release|Any CPU
{C35F1536-7DE5-4F9D-9604-B5B4E1561947}.ReleaseAOT|Any CPU.ActiveCfg = ReleaseAOT|Any CPU
{C35F1536-7DE5-4F9D-9604-B5B4E1561947}.ReleaseAOT|Any CPU.Build.0 = ReleaseAOT|Any CPU
{B9AECA11-E248-4886-A10B-81B631CAAF29}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B9AECA11-E248-4886-A10B-81B631CAAF29}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B9AECA11-E248-4886-A10B-81B631CAAF29}.DebugAOT|Any CPU.ActiveCfg = DebugAOT|Any CPU
{B9AECA11-E248-4886-A10B-81B631CAAF29}.DebugAOT|Any CPU.Build.0 = DebugAOT|Any CPU
{B9AECA11-E248-4886-A10B-81B631CAAF29}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B9AECA11-E248-4886-A10B-81B631CAAF29}.Release|Any CPU.Build.0 = Release|Any CPU
{B9AECA11-E248-4886-A10B-81B631CAAF29}.ReleaseAOT|Any CPU.ActiveCfg = ReleaseAOT|Any CPU
{B9AECA11-E248-4886-A10B-81B631CAAF29}.ReleaseAOT|Any CPU.Build.0 = ReleaseAOT|Any CPU
{81BB2C11-9408-4EA3-822E-42987AF54429}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{81BB2C11-9408-4EA3-822E-42987AF54429}.Debug|Any CPU.Build.0 = Debug|Any CPU
{81BB2C11-9408-4EA3-822E-42987AF54429}.DebugAOT|Any CPU.ActiveCfg = DebugAOT|Any CPU
{81BB2C11-9408-4EA3-822E-42987AF54429}.DebugAOT|Any CPU.Build.0 = DebugAOT|Any CPU
{81BB2C11-9408-4EA3-822E-42987AF54429}.Release|Any CPU.ActiveCfg = Release|Any CPU
{81BB2C11-9408-4EA3-822E-42987AF54429}.Release|Any CPU.Build.0 = Release|Any CPU
{81BB2C11-9408-4EA3-822E-42987AF54429}.ReleaseAOT|Any CPU.ActiveCfg = ReleaseAOT|Any CPU
{81BB2C11-9408-4EA3-822E-42987AF54429}.ReleaseAOT|Any CPU.Build.0 = ReleaseAOT|Any CPU
{FD4A2C14-8E3D-4957-ABBE-3C38897B3E2D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FD4A2C14-8E3D-4957-ABBE-3C38897B3E2D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FD4A2C14-8E3D-4957-ABBE-3C38897B3E2D}.DebugAOT|Any CPU.ActiveCfg = DebugAOT|Any CPU
{FD4A2C14-8E3D-4957-ABBE-3C38897B3E2D}.DebugAOT|Any CPU.Build.0 = DebugAOT|Any CPU
{FD4A2C14-8E3D-4957-ABBE-3C38897B3E2D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FD4A2C14-8E3D-4957-ABBE-3C38897B3E2D}.Release|Any CPU.Build.0 = Release|Any CPU
{FD4A2C14-8E3D-4957-ABBE-3C38897B3E2D}.ReleaseAOT|Any CPU.ActiveCfg = ReleaseAOT|Any CPU
{FD4A2C14-8E3D-4957-ABBE-3C38897B3E2D}.ReleaseAOT|Any CPU.Build.0 = ReleaseAOT|Any CPU
{0BE11899-DF2D-4BDE-B9EE-2489E8D35E7D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0BE11899-DF2D-4BDE-B9EE-2489E8D35E7D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0BE11899-DF2D-4BDE-B9EE-2489E8D35E7D}.DebugAOT|Any CPU.ActiveCfg = DebugAOT|Any CPU
{0BE11899-DF2D-4BDE-B9EE-2489E8D35E7D}.DebugAOT|Any CPU.Build.0 = DebugAOT|Any CPU
{0BE11899-DF2D-4BDE-B9EE-2489E8D35E7D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0BE11899-DF2D-4BDE-B9EE-2489E8D35E7D}.Release|Any CPU.Build.0 = Release|Any CPU
{0BE11899-DF2D-4BDE-B9EE-2489E8D35E7D}.ReleaseAOT|Any CPU.ActiveCfg = ReleaseAOT|Any CPU
{0BE11899-DF2D-4BDE-B9EE-2489E8D35E7D}.ReleaseAOT|Any CPU.Build.0 = ReleaseAOT|Any CPU
{716364DE-B988-41A6-BAB4-327964266ECC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{716364DE-B988-41A6-BAB4-327964266ECC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{716364DE-B988-41A6-BAB4-327964266ECC}.DebugAOT|Any CPU.ActiveCfg = DebugAOT|Any CPU
{716364DE-B988-41A6-BAB4-327964266ECC}.DebugAOT|Any CPU.Build.0 = DebugAOT|Any CPU
{716364DE-B988-41A6-BAB4-327964266ECC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{716364DE-B988-41A6-BAB4-327964266ECC}.Release|Any CPU.Build.0 = Release|Any CPU
{716364DE-B988-41A6-BAB4-327964266ECC}.ReleaseAOT|Any CPU.ActiveCfg = ReleaseAOT|Any CPU
{716364DE-B988-41A6-BAB4-327964266ECC}.ReleaseAOT|Any CPU.Build.0 = ReleaseAOT|Any CPU
{C16F112F-38C3-40BC-9F5F-4791112063D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C16F112F-38C3-40BC-9F5F-4791112063D6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C16F112F-38C3-40BC-9F5F-4791112063D6}.DebugAOT|Any CPU.ActiveCfg = DebugAOT|Any CPU
{C16F112F-38C3-40BC-9F5F-4791112063D6}.DebugAOT|Any CPU.Build.0 = DebugAOT|Any CPU
{C16F112F-38C3-40BC-9F5F-4791112063D6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C16F112F-38C3-40BC-9F5F-4791112063D6}.Release|Any CPU.Build.0 = Release|Any CPU
{C16F112F-38C3-40BC-9F5F-4791112063D6}.ReleaseAOT|Any CPU.ActiveCfg = ReleaseAOT|Any CPU
{C16F112F-38C3-40BC-9F5F-4791112063D6}.ReleaseAOT|Any CPU.Build.0 = ReleaseAOT|Any CPU
{DFAB6F2D-B9BF-4AFF-B22B-7684A328EBA3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DFAB6F2D-B9BF-4AFF-B22B-7684A328EBA3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DFAB6F2D-B9BF-4AFF-B22B-7684A328EBA3}.DebugAOT|Any CPU.ActiveCfg = DebugAOT|Any CPU
{DFAB6F2D-B9BF-4AFF-B22B-7684A328EBA3}.DebugAOT|Any CPU.Build.0 = DebugAOT|Any CPU
{DFAB6F2D-B9BF-4AFF-B22B-7684A328EBA3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DFAB6F2D-B9BF-4AFF-B22B-7684A328EBA3}.Release|Any CPU.Build.0 = Release|Any CPU
{DFAB6F2D-B9BF-4AFF-B22B-7684A328EBA3}.ReleaseAOT|Any CPU.ActiveCfg = ReleaseAOT|Any CPU
{DFAB6F2D-B9BF-4AFF-B22B-7684A328EBA3}.ReleaseAOT|Any CPU.Build.0 = ReleaseAOT|Any CPU
{2D5D3A1D-5730-4648-B0AB-06C53CB910C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2D5D3A1D-5730-4648-B0AB-06C53CB910C0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2D5D3A1D-5730-4648-B0AB-06C53CB910C0}.DebugAOT|Any CPU.ActiveCfg = DebugAOT|Any CPU
{2D5D3A1D-5730-4648-B0AB-06C53CB910C0}.DebugAOT|Any CPU.Build.0 = DebugAOT|Any CPU
{2D5D3A1D-5730-4648-B0AB-06C53CB910C0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2D5D3A1D-5730-4648-B0AB-06C53CB910C0}.Release|Any CPU.Build.0 = Release|Any CPU
{2D5D3A1D-5730-4648-B0AB-06C53CB910C0}.ReleaseAOT|Any CPU.ActiveCfg = ReleaseAOT|Any CPU
{2D5D3A1D-5730-4648-B0AB-06C53CB910C0}.ReleaseAOT|Any CPU.Build.0 = ReleaseAOT|Any CPU
{D99A395A-8569-4DB0-B336-900647890052}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D99A395A-8569-4DB0-B336-900647890052}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D99A395A-8569-4DB0-B336-900647890052}.DebugAOT|Any CPU.ActiveCfg = DebugAOT|Any CPU
{D99A395A-8569-4DB0-B336-900647890052}.DebugAOT|Any CPU.Build.0 = DebugAOT|Any CPU
{D99A395A-8569-4DB0-B336-900647890052}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D99A395A-8569-4DB0-B336-900647890052}.Release|Any CPU.Build.0 = Release|Any CPU
{D99A395A-8569-4DB0-B336-900647890052}.ReleaseAOT|Any CPU.ActiveCfg = ReleaseAOT|Any CPU
{D99A395A-8569-4DB0-B336-900647890052}.ReleaseAOT|Any CPU.Build.0 = ReleaseAOT|Any CPU
{BEE1C184-C9A4-410B-8DFC-FB74D5C93AEB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BEE1C184-C9A4-410B-8DFC-FB74D5C93AEB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BEE1C184-C9A4-410B-8DFC-FB74D5C93AEB}.DebugAOT|Any CPU.ActiveCfg = DebugAOT|Any CPU
{BEE1C184-C9A4-410B-8DFC-FB74D5C93AEB}.DebugAOT|Any CPU.Build.0 = DebugAOT|Any CPU
{BEE1C184-C9A4-410B-8DFC-FB74D5C93AEB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BEE1C184-C9A4-410B-8DFC-FB74D5C93AEB}.Release|Any CPU.Build.0 = Release|Any CPU
{BEE1C184-C9A4-410B-8DFC-FB74D5C93AEB}.ReleaseAOT|Any CPU.ActiveCfg = ReleaseAOT|Any CPU
{BEE1C184-C9A4-410B-8DFC-FB74D5C93AEB}.ReleaseAOT|Any CPU.Build.0 = ReleaseAOT|Any CPU
{7C1B2721-13DA-4B62-B046-C626605ECCE6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7C1B2721-13DA-4B62-B046-C626605ECCE6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7C1B2721-13DA-4B62-B046-C626605ECCE6}.DebugAOT|Any CPU.ActiveCfg = DebugAOT|Any CPU
{7C1B2721-13DA-4B62-B046-C626605ECCE6}.DebugAOT|Any CPU.Build.0 = DebugAOT|Any CPU
{7C1B2721-13DA-4B62-B046-C626605ECCE6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7C1B2721-13DA-4B62-B046-C626605ECCE6}.Release|Any CPU.Build.0 = Release|Any CPU
{7C1B2721-13DA-4B62-B046-C626605ECCE6}.ReleaseAOT|Any CPU.ActiveCfg = ReleaseAOT|Any CPU
{7C1B2721-13DA-4B62-B046-C626605ECCE6}.ReleaseAOT|Any CPU.Build.0 = ReleaseAOT|Any CPU
{BA161CA0-CD65-4E6E-B644-51C8D1E542DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BA161CA0-CD65-4E6E-B644-51C8D1E542DC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BA161CA0-CD65-4E6E-B644-51C8D1E542DC}.DebugAOT|Any CPU.ActiveCfg = DebugAOT|Any CPU
{BA161CA0-CD65-4E6E-B644-51C8D1E542DC}.DebugAOT|Any CPU.Build.0 = DebugAOT|Any CPU
{BA161CA0-CD65-4E6E-B644-51C8D1E542DC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BA161CA0-CD65-4E6E-B644-51C8D1E542DC}.Release|Any CPU.Build.0 = Release|Any CPU
{BA161CA0-CD65-4E6E-B644-51C8D1E542DC}.ReleaseAOT|Any CPU.ActiveCfg = ReleaseAOT|Any CPU
{BA161CA0-CD65-4E6E-B644-51C8D1E542DC}.ReleaseAOT|Any CPU.Build.0 = ReleaseAOT|Any CPU
{6AE2A5E8-4C5A-48B9-997B-E1455C0355C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6AE2A5E8-4C5A-48B9-997B-E1455C0355C6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6AE2A5E8-4C5A-48B9-997B-E1455C0355C6}.DebugAOT|Any CPU.ActiveCfg = DebugAOT|Any CPU
{6AE2A5E8-4C5A-48B9-997B-E1455C0355C6}.DebugAOT|Any CPU.Build.0 = DebugAOT|Any CPU
{6AE2A5E8-4C5A-48B9-997B-E1455C0355C6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6AE2A5E8-4C5A-48B9-997B-E1455C0355C6}.Release|Any CPU.Build.0 = Release|Any CPU
{6AE2A5E8-4C5A-48B9-997B-E1455C0355C6}.ReleaseAOT|Any CPU.ActiveCfg = ReleaseAOT|Any CPU
{6AE2A5E8-4C5A-48B9-997B-E1455C0355C6}.ReleaseAOT|Any CPU.Build.0 = ReleaseAOT|Any CPU
{D4D09B08-D580-4D69-B886-C35D2853F6C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D4D09B08-D580-4D69-B886-C35D2853F6C8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D4D09B08-D580-4D69-B886-C35D2853F6C8}.DebugAOT|Any CPU.ActiveCfg = DebugAOT|Any CPU
{D4D09B08-D580-4D69-B886-C35D2853F6C8}.DebugAOT|Any CPU.Build.0 = DebugAOT|Any CPU
{D4D09B08-D580-4D69-B886-C35D2853F6C8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D4D09B08-D580-4D69-B886-C35D2853F6C8}.Release|Any CPU.Build.0 = Release|Any CPU
{D4D09B08-D580-4D69-B886-C35D2853F6C8}.ReleaseAOT|Any CPU.ActiveCfg = ReleaseAOT|Any CPU
{D4D09B08-D580-4D69-B886-C35D2853F6C8}.ReleaseAOT|Any CPU.Build.0 = ReleaseAOT|Any CPU
{2BCB3D7A-38C0-4FE7-8FDA-374C6AD56D0E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2BCB3D7A-38C0-4FE7-8FDA-374C6AD56D0E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2BCB3D7A-38C0-4FE7-8FDA-374C6AD56D0E}.DebugAOT|Any CPU.ActiveCfg = DebugAOT|Any CPU
{2BCB3D7A-38C0-4FE7-8FDA-374C6AD56D0E}.DebugAOT|Any CPU.Build.0 = DebugAOT|Any CPU
{2BCB3D7A-38C0-4FE7-8FDA-374C6AD56D0E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2BCB3D7A-38C0-4FE7-8FDA-374C6AD56D0E}.Release|Any CPU.Build.0 = Release|Any CPU
{2BCB3D7A-38C0-4FE7-8FDA-374C6AD56D0E}.ReleaseAOT|Any CPU.ActiveCfg = ReleaseAOT|Any CPU
{2BCB3D7A-38C0-4FE7-8FDA-374C6AD56D0E}.ReleaseAOT|Any CPU.Build.0 = ReleaseAOT|Any CPU
{77D01AD9-2C98-478E-AE1D-8F7100738FB4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{77D01AD9-2C98-478E-AE1D-8F7100738FB4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{77D01AD9-2C98-478E-AE1D-8F7100738FB4}.DebugAOT|Any CPU.ActiveCfg = DebugAOT|Any CPU
{77D01AD9-2C98-478E-AE1D-8F7100738FB4}.DebugAOT|Any CPU.Build.0 = DebugAOT|Any CPU
{77D01AD9-2C98-478E-AE1D-8F7100738FB4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{77D01AD9-2C98-478E-AE1D-8F7100738FB4}.Release|Any CPU.Build.0 = Release|Any CPU
{77D01AD9-2C98-478E-AE1D-8F7100738FB4}.ReleaseAOT|Any CPU.ActiveCfg = ReleaseAOT|Any CPU
{77D01AD9-2C98-478E-AE1D-8F7100738FB4}.ReleaseAOT|Any CPU.Build.0 = ReleaseAOT|Any CPU
{77F96ECE-4952-42DB-A528-DED25572A573}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{77F96ECE-4952-42DB-A528-DED25572A573}.Debug|Any CPU.Build.0 = Debug|Any CPU
{77F96ECE-4952-42DB-A528-DED25572A573}.DebugAOT|Any CPU.ActiveCfg = DebugAOT|Any CPU
{77F96ECE-4952-42DB-A528-DED25572A573}.DebugAOT|Any CPU.Build.0 = DebugAOT|Any CPU
{77F96ECE-4952-42DB-A528-DED25572A573}.Release|Any CPU.ActiveCfg = Release|Any CPU
{77F96ECE-4952-42DB-A528-DED25572A573}.Release|Any CPU.Build.0 = Release|Any CPU
{77F96ECE-4952-42DB-A528-DED25572A573}.ReleaseAOT|Any CPU.ActiveCfg = ReleaseAOT|Any CPU
{77F96ECE-4952-42DB-A528-DED25572A573}.ReleaseAOT|Any CPU.Build.0 = ReleaseAOT|Any CPU
{AF34127A-3A92-43E5-8496-14960A50B1F1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AF34127A-3A92-43E5-8496-14960A50B1F1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AF34127A-3A92-43E5-8496-14960A50B1F1}.DebugAOT|Any CPU.ActiveCfg = DebugAOT|Any CPU
{AF34127A-3A92-43E5-8496-14960A50B1F1}.DebugAOT|Any CPU.Build.0 = DebugAOT|Any CPU
{AF34127A-3A92-43E5-8496-14960A50B1F1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AF34127A-3A92-43E5-8496-14960A50B1F1}.Release|Any CPU.Build.0 = Release|Any CPU
{AF34127A-3A92-43E5-8496-14960A50B1F1}.ReleaseAOT|Any CPU.ActiveCfg = ReleaseAOT|Any CPU
{AF34127A-3A92-43E5-8496-14960A50B1F1}.ReleaseAOT|Any CPU.Build.0 = ReleaseAOT|Any CPU
{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}.DebugAOT|Any CPU.ActiveCfg = DebugAOT|Any CPU
{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}.DebugAOT|Any CPU.Build.0 = DebugAOT|Any CPU
{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}.Release|Any CPU.Build.0 = Release|Any CPU
{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}.ReleaseAOT|Any CPU.ActiveCfg = ReleaseAOT|Any CPU
{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}.ReleaseAOT|Any CPU.Build.0 = ReleaseAOT|Any CPU
{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.DebugAOT|Any CPU.ActiveCfg = DebugAOT|Any CPU
{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.DebugAOT|Any CPU.Build.0 = DebugAOT|Any CPU
{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Release|Any CPU.Build.0 = Release|Any CPU
{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.ReleaseAOT|Any CPU.ActiveCfg = ReleaseAOT|Any CPU
{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.ReleaseAOT|Any CPU.Build.0 = ReleaseAOT|Any CPU
{C08931FA-1191-417A-864F-3882D93E683B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C08931FA-1191-417A-864F-3882D93E683B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C08931FA-1191-417A-864F-3882D93E683B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C08931FA-1191-417A-864F-3882D93E683B}.Release|Any CPU.Build.0 = Release|Any CPU
{4A89A234-4F19-497D-A576-DDE8CDFC5B22}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4A89A234-4F19-497D-A576-DDE8CDFC5B22}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4A89A234-4F19-497D-A576-DDE8CDFC5B22}.DebugAOT|Any CPU.ActiveCfg = DebugAOT|Any CPU
{4A89A234-4F19-497D-A576-DDE8CDFC5B22}.DebugAOT|Any CPU.Build.0 = DebugAOT|Any CPU
{4A89A234-4F19-497D-A576-DDE8CDFC5B22}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4A89A234-4F19-497D-A576-DDE8CDFC5B22}.ReleaseAOT|Any CPU.ActiveCfg = ReleaseAOT|Any CPU
{81EA598C-DBA1-40B0-8DA4-4796B78F2037}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{81EA598C-DBA1-40B0-8DA4-4796B78F2037}.Debug|Any CPU.Build.0 = Debug|Any CPU
{81EA598C-DBA1-40B0-8DA4-4796B78F2037}.Release|Any CPU.ActiveCfg = Release|Any CPU
{81EA598C-DBA1-40B0-8DA4-4796B78F2037}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@@ -332,7 +332,6 @@
0100E680149DC000,"Arcaea",,playable,2023-03-16 19:31:21
01003C2010C78000,"Archaica: The Path Of Light",crash,nothing,2020-10-16 13:22:26
01004DA012976000,"Area 86",,playable,2020-12-16 16:45:52
01008d8006a6a000,"Arena of Valor",crash,boots,2025-02-03 22:19:34
0100691013C46000,"ARIA CHRONICLE",,playable,2022-11-16 13:50:55
0100D4A00B284000,"ARK: Survival Evolved",gpu;nvdec;online-broken;UE4;ldn-untested,ingame,2024-04-16 00:53:56
0100C56012C96000,"Arkanoid vs. Space Invaders",services,ingame,2021-01-21 12:50:30
@@ -427,7 +426,6 @@
0100E48013A34000,"Balan Wonderworld Demo",gpu;services;UE4;demo,ingame,2023-02-16 20:05:07
0100CD801CE5E000,"Balatro",,ingame,2024-04-21 02:01:53
010010A00DA48000,"Baldur's Gate and Baldur's Gate II: Enhanced Editions",32-bit,playable,2022-09-12 23:52:15
0100fd1014726000,"Baldur's Gate: Dark Alliance",ldn-untested,ingame,2025-02-03 22:21:00
0100BC400FB64000,"Balthazar's Dream",,playable,2022-09-13 00:13:22
01008D30128E0000,"Bamerang",,playable,2022-10-26 00:29:39
010013C010C5C000,"Banner of the Maid",,playable,2021-06-14 15:23:37
@@ -530,7 +528,6 @@
01005950022EC000,"Blade Strangers",nvdec,playable,2022-07-17 19:02:43
0100DF0011A6A000,"Bladed Fury",,playable,2022-10-26 11:36:26
0100CFA00CC74000,"Blades of Time",deadlock;online,boots,2022-07-17 19:19:58
01003d700dd8a000,"Blades",,boots,2025-02-03 22:22:00
01006CC01182C000,"Blair Witch",nvdec;UE4,playable,2022-10-01 14:06:16
010039501405E000,"Blanc",gpu;slow,ingame,2023-02-22 14:00:13
0100698009C6E000,"Blasphemous",nvdec,playable,2021-03-01 12:15:31
@@ -631,7 +628,6 @@
010030D012FF6000,"Bus Driver Simulator",,playable,2022-10-17 13:55:27
0100A9101418C000,"BUSTAFELLOWS",nvdec,playable,2020-10-17 20:04:41
0100177005C8A000,"BUTCHER",,playable,2021-01-11 18:50:17
01008c2019598000,"Bluey: The Videogame",,playable,2025-02-11 04:38:00
01000B900D8B0000,"Cadence of Hyrule: Crypt of the NecroDancer Featuring The Legend of Zelda",slow;nvdec,playable,2024-04-01 22:43:40
010065700EE06000,"Cadence of Hyrule: Crypt of the NecroDancer Featuring The Legend of Zelda Demo",demo;gpu;nvdec,ingame,2021-02-14 21:48:15
01005C00117A8000,"Café Enchanté",,playable,2020-11-13 14:54:25
@@ -959,7 +955,7 @@
010012800EBAE000,"Disney TSUM TSUM FESTIVAL",crash,menus,2020-07-14 14:05:28
01009740120FE000,"DISTRAINT 2",,playable,2020-09-03 16:08:12
010075B004DD2000,"DISTRAINT: Deluxe Edition",,playable,2020-06-15 23:42:24
010027400CDC6000,"Divinity: Original Sin 2 - Definitive Edition",services;crash;online-broken;regression,ingame,2025-02-03 22:12:30
010027400CDC6000,"Divinity: Original Sin 2 - Definitive Edition",services;crash;online-broken;regression,menus,2023-08-13 17:20:03
01001770115C8000,"Dodo Peak",nvdec;UE4,playable,2022-10-04 16:13:05
010077B0100DA000,"Dogurai",,playable,2020-10-04 02:40:16
010048100D51A000,"Dokapon Up! Mugen no Roulette",gpu;Needs Update,menus,2022-12-08 19:39:10
@@ -970,7 +966,6 @@
0100751007ADA000,"Don't Starve: Nintendo Switch Edition",nvdec,playable,2022-02-05 20:43:34
010088B010DD2000,"Dongo Adventure",,playable,2022-10-04 16:22:26
0100C1F0051B6000,"Donkey Kong Country™: Tropical Freeze",,playable,2024-08-05 16:46:10
01009D901BC56000,"Donkey Kong Country™: Returns HD",gpu,ingame,2025-02-16 13:44:12
0100F2C00F060000,"Doodle Derby",,boots,2020-12-04 22:51:48
0100416004C00000,"DOOM",gpu;slow;nvdec;online-broken,ingame,2024-09-23 15:40:07
010018900DD00000,"DOOM (1993)",nvdec;online-broken,menus,2022-09-06 13:32:19
@@ -1160,7 +1155,7 @@
010095600AA36000,"Fill-a-Pix: Phil's Epic Adventure",,playable,2020-12-22 13:48:22
0100C3A00BB76000,"Fimbul",nvdec,playable,2022-07-26 13:31:47
0100C8200E942000,"Fin and the Ancient Mystery",nvdec,playable,2020-12-17 16:40:39
01000EA014150000,"FINAL FANTASY",,playable,2025-02-16 21:27:30
01000EA014150000,"FINAL FANTASY",crash,nothing,2024-09-05 20:55:30
01006B7014156000,"FINAL FANTASY II",crash,nothing,2024-04-13 19:18:04
01006F000B056000,"FINAL FANTASY IX",audout;nvdec,playable,2021-06-05 11:35:00
0100AA201415C000,"FINAL FANTASY V",,playable,2023-04-26 01:11:55
@@ -1251,7 +1246,7 @@
0100A6B00D4EC000,"Furwind",,playable,2021-02-19 19:44:08
0100ECE00C0C4000,"Fury Unleashed",crash;services,ingame,2020-10-18 11:52:40
010070000ED9E000,"Fury Unleashed Demo",,playable,2020-10-08 20:09:21
0100E1F013674000,"FUSER™",nvdec;UE4;slow;gpu,ingame,2025-02-12 16:03:00
0100E1F013674000,"FUSER™",nvdec;UE4,playable,2022-10-17 20:58:32
0100A7A015E4C000,"Fushigi no Gensokyo Lotus Labyrinth",Needs Update;audio;gpu;nvdec,ingame,2021-01-20 15:30:02
01003C300B274000,"Futari de! Nyanko Daisensou",,playable,2024-01-05 22:26:52
010055801134E000,"FUZE Player",online-broken;vulkan-backend-bug,ingame,2022-10-18 12:23:53
@@ -1383,9 +1378,6 @@
0100763015C2E000,"Gunvolt Chronicles: Luminous Avenger iX 2",crash;Needs Update,nothing,2022-04-29 15:34:34
01002C8018554000,"Gurimugurimoa OnceMore Demo",,playable,2022-07-29 22:07:31
0100AC601DCA8000,"GYLT",crash,ingame,2024-03-18 20:16:51
0100c3c012718000,"Grand Theft Auto: III The Definitive Edition",gpu;UE4,ingame,2022-10-31 20:13:52
0100182014022000,"Grand Theft Auto: Vice City The Definitive Edition",gpu;UE4,ingame,2022-10-31 20:13:52
010065a014024000,"Grand Theft Auto: San Andreas The Definitive Edition",gpu;UE4,ingame,2022-10-31 20:13:52
0100822012D76000,"HAAK",gpu,ingame,2023-02-19 14:31:05
01007E100EFA8000,"Habroxia",,playable,2020-06-16 23:04:42
0100535012974000,"Hades",vulkan,playable,2022-10-05 10:45:21
@@ -1440,7 +1432,7 @@
010083A018262000,"Hitman: Blood Money — Reprisal",deadlock,ingame,2024-09-28 16:28:50
01004B100A5CC000,"Hob: The Definitive Edition",,playable,2021-01-13 09:39:19
0100F7300ED2C000,"Hoggy2",,playable,2022-10-10 13:53:35
0100F7E00C70E000,"Hogwarts Legacy",UE4;slow,ingame,2024-09-03 19:53:58
0100F7E00C70E000,"Hogwarts Legacy",slow,ingame,2024-09-03 19:53:58
0100633007D48000,"Hollow Knight",nvdec,playable,2023-01-16 15:44:56
0100F2100061E800,"Hollow0",UE4;gpu,ingame,2021-03-03 23:42:56
0100342009E16000,"Holy Potatoes! What The Hell?!",,playable,2020-07-03 10:48:56
@@ -1662,7 +1654,7 @@
0100A73006E74000,"Legendary Eleven",,playable,2021-06-08 12:09:03
0100A7700B46C000,"Legendary Fishing",online,playable,2021-04-14 15:08:46
0100739018020000,"LEGO® 2K Drive",gpu;ldn-works,ingame,2024-04-09 02:05:12
010085500130a000,"LEGO® CITY Undercover",nvdec,playable,2024-09-30 08:44:27
01003A30012C0000,"LEGO® CITY Undercover",nvdec,playable,2024-09-30 08:44:27
010070D009FEC000,"LEGO® DC Super-Villains",,playable,2021-05-27 18:10:37
010052A00B5D2000,"LEGO® Harry Potter™ Collection",crash,ingame,2024-01-31 10:28:07
010073C01AF34000,"LEGO® Horizon Adventures™",vulkan-backend-bug;opengl-backend-bug;UE4,ingame,2025-01-07 04:24:56
@@ -1804,7 +1796,6 @@
010005A00B312000,"Megaton Rainfall",gpu;opengl,boots,2022-08-04 18:29:43
0100EA100DF92000,"Meiji Katsugeki Haikara Ryuuseigumi - Seibai Shimaseu, Yonaoshi Kagyou",32-bit;nvdec,playable,2022-12-05 13:19:12
0100B360068B2000,"Mekorama",gpu,boots,2021-06-17 16:37:21
010012301932A000,"Melatonin",,playable,2025-02-16 04:08:17
01000FA010340000,"Melbits World",nvdec;online,menus,2021-11-26 13:51:22
0100F68019636000,"Melon Journey",,playable,2023-04-23 21:20:01
010079C012896000,"Memories Off -Innocent Fille- for Dearest",,playable,2020-08-04 07:31:22
@@ -1922,7 +1913,6 @@
010073E008E6E000,"Mugsters",,playable,2021-01-28 17:57:17
0100A8400471A000,"MUJO",,playable,2020-05-08 16:31:04
0100211005E94000,"Mulaka",,playable,2021-01-28 18:07:20
01008e2013fb4000,"Multi Quiz",ldn-untested,ingame,2025-02-03 22:26:00
010038B00B9AE000,"Mummy Pinball",,playable,2022-08-05 16:08:11
01008E200C5C2000,"Muse Dash",,playable,2020-06-06 14:41:29
010035901046C000,"Mushroom Quest",,playable,2020-05-17 13:07:08
@@ -2038,7 +2028,6 @@
010003C00B868000,"Ninjin: Clash of Carrots",online-broken,playable,2024-07-10 05:12:26
0100746010E4C000,"NinNinDays",,playable,2022-11-20 15:17:29
0100C9A00ECE6000,"Nintendo 64™ Nintendo Switch Online",gpu;vulkan,ingame,2024-04-23 20:21:07
0100e0601c632000,"Nintendo 64™ Nintendo Switch Online: MATURE 17+",,ingame,2025-02-03 22:27:00
0100D870045B6000,"Nintendo Entertainment System™ - Nintendo Switch Online",online,playable,2022-07-01 15:45:06
0100C4B0034B2000,"Nintendo Labo Toy-Con 01 Variety Kit",gpu,ingame,2022-08-07 12:56:07
01001E9003502000,"Nintendo Labo Toy-Con 03 Vehicle Kit",services;crash,menus,2022-08-03 17:20:11
@@ -2069,7 +2058,7 @@
010002700C34C000,"Numbala",,playable,2020-05-11 12:01:07
010020500C8C8000,"Number Place 10000",gpu,menus,2021-11-24 09:14:23
010003701002C000,"Nurse Love Syndrome",,playable,2022-10-13 10:05:22
,"nx-hbmenu",Needs Update;homebrew,boots,2024-04-06 22:05:32
0000000000000000,"nx-hbmenu",Needs Update;homebrew,boots,2024-04-06 22:05:32
,"nxquake2",services;crash;homebrew,nothing,2022-08-04 23:14:04
010049F00EC30000,"Nyan Cat: Lost in Space",online,playable,2021-06-12 13:22:03
01002E6014FC4000,"O---O",,playable,2022-10-29 12:12:14
@@ -2477,7 +2466,7 @@
0100AFE00DDAC000,"Royal Roads",,playable,2020-11-17 12:54:38
0100E2C00B414000,"RPG Maker MV",nvdec,playable,2021-01-05 20:12:01
01005CD015986000,"rRootage Reloaded",,playable,2022-08-05 23:20:18
,"RSDKv5u",homebrew,ingame,2024-04-01 16:25:34
0000000000000000,"RSDKv5u",homebrew,ingame,2024-04-01 16:25:34
010009B00D33C000,"Rugby Challenge 4",slow;online-broken;UE4,playable,2022-10-06 12:45:53
01006EC00F2CC000,"RUINER",UE4,playable,2022-10-03 14:11:33
010074F00DE4A000,"Run the Fan",,playable,2021-02-27 13:36:28
@@ -2486,7 +2475,6 @@
010081C0191D8000,"Rune Factory 3 Special",,playable,2023-10-15 08:32:49
010051D00E3A4000,"Rune Factory 4 Special",32-bit;crash;nvdec,ingame,2023-05-06 08:49:17
010014D01216E000,"Rune Factory 5 (JP)",gpu,ingame,2021-06-01 12:00:36
010071E0145F8000,"Rustler",,playable,2025-02-10 20:17:12
0100E21013908000,"RWBY: Grimm Eclipse - Definitive Edition",online-broken,playable,2022-11-03 10:44:01
010012C0060F0000,"RXN -Raijin-",nvdec,playable,2021-01-10 16:05:43
0100B8B012ECA000,"S.N.I.P.E.R. - Hunter Scope",,playable,2021-04-19 15:58:09
@@ -2544,7 +2532,7 @@
0100C3E00B700000,"SEGA AGES Space Harrier",,playable,2021-01-11 12:57:40
010054400D2E6000,"SEGA AGES Virtua Racing",online-broken,playable,2023-01-29 17:08:39
01001E700AC60000,"SEGA AGES Wonder Boy: Monster Land",online,playable,2021-05-05 16:28:25
0100B3C014BDA000,"SEGA Genesis™ Nintendo Switch Online",crash;regression,ingame,2025-02-03 22:13:30
0100B3C014BDA000,"SEGA Genesis™ Nintendo Switch Online",crash;regression,nothing,2022-04-11 07:27:21
0100F7300B24E000,"SEGA Mega Drive Classics",online,playable,2021-01-05 11:08:00
01009840046BC000,"Semispheres",,playable,2021-01-06 23:08:31
0100D1800D902000,"SENRAN KAGURA Peach Ball",,playable,2021-06-03 15:12:10
@@ -2680,10 +2668,10 @@
01004F401BEBE000,"Song of Nunu: A League of Legends Story",,ingame,2024-07-12 18:53:44
0100E5400BF94000,"Songbird Symphony",,playable,2021-02-27 02:44:04
010031D00A604000,"Songbringer",,playable,2020-06-22 10:42:02
,"Sonic 1 (2013)",crash;homebrew,ingame,2024-04-06 18:31:20
,"Sonic 2 (2013)",crash;homebrew,ingame,2024-04-01 16:25:30
,"Sonic A.I.R",homebrew,ingame,2024-04-01 16:25:32
,"Sonic CD",crash;homebrew,ingame,2024-04-01 16:25:31
0000000000000000,"Sonic 1 (2013)",crash;homebrew,ingame,2024-04-06 18:31:20
0000000000000000,"Sonic 2 (2013)",crash;homebrew,ingame,2024-04-01 16:25:30
0000000000000000,"Sonic A.I.R",homebrew,ingame,2024-04-01 16:25:32
0000000000000000,"Sonic CD",crash;homebrew,ingame,2024-04-01 16:25:31
010040E0116B8000,"Sonic Colors: Ultimate",,playable,2022-11-12 21:24:26
01001270012B6000,"SONIC FORCES™",,playable,2024-07-28 13:11:21
01004AD014BF0000,"Sonic Frontiers",gpu;deadlock;amd-vendor-bug;intel-vendor-bug,ingame,2024-09-05 09:18:53
@@ -2700,7 +2688,7 @@
0100707011722000,"Space Elite Force",,playable,2020-11-27 15:21:05
010047B010260000,"Space Pioneer",,playable,2022-10-20 12:24:37
010010A009830000,"Space Ribbon",,playable,2022-08-15 17:17:10
,"SpaceCadetPinball",homebrew,ingame,2024-04-18 19:30:04
0000000000000000,"SpaceCadetPinball",homebrew,ingame,2024-04-18 19:30:04
0100D9B0041CE000,"Spacecats with Lasers",,playable,2022-08-15 17:22:44
010034800FB60000,"Spaceland",,playable,2020-11-01 14:31:56
010028D0045CE000,"Sparkle 2",,playable,2020-10-19 11:51:39
@@ -2733,7 +2721,7 @@
0100C2500FC20000,"Splatoon™ 3",ldn-works;opengl-backend-bug;LAN;amd-vendor-bug,playable,2024-08-04 23:49:11
0100BA0018500000,"Splatoon™ 3: Splatfest World Premiere",gpu;online-broken;demo,ingame,2022-09-19 03:17:12
010062800D39C000,"SpongeBob SquarePants: Battle for Bikini Bottom - Rehydrated",online-broken;UE4;ldn-broken;vulkan-backend-bug,playable,2023-08-01 19:29:34
01009FB0172F4000,"SpongeBob SquarePants: The Cosmic Shake",gpu;UE4,ingame,2024-03-04 16:35:00
01009FB0172F4000,"SpongeBob SquarePants: The Cosmic Shake",gpu;UE4,ingame,2023-08-01 19:29:53
010097C01336A000,"Spooky Chase",,playable,2022-11-04 12:17:44
0100C6100D75E000,"Spooky Ghosts Dot Com",,playable,2021-06-15 15:16:11
0100DE9005170000,"Sports Party",nvdec,playable,2021-03-05 13:40:42
@@ -2844,9 +2832,8 @@
01009B90006DC000,"Super Mario Maker™ 2",online-broken;ldn-broken,playable,2024-08-25 11:05:19
0100000000010000,"Super Mario Odyssey™",nvdec;intel-vendor-bug;mac-bug,playable,2024-08-25 01:32:34
010036B0034E4000,"Super Mario Party™",gpu;Needs Update;ldn-works,ingame,2024-06-21 05:10:16
0100965017338000,"Super Mario Party Jamboree",mac-bug;gpu,ingame,2025-02-17 02:09:20
0100BC0018138000,"Super Mario RPG™",gpu;audio;nvdec,ingame,2024-06-19 17:43:42
,"Super Mario World",homebrew,boots,2024-06-13 01:40:31
0000000000000000,"Super Mario World",homebrew,boots,2024-06-13 01:40:31
010049900F546000,"Super Mario™ 3D All-Stars",services-horizon;slow;vulkan;amd-vendor-bug,ingame,2024-05-07 02:38:16
010028600EBDA000,"Super Mario™ 3D World + Bowsers Fury",ldn-works,playable,2024-07-31 10:45:37
01004F8006A78000,"Super Meat Boy",services,playable,2020-04-02 23:10:07
@@ -2977,7 +2964,6 @@
0100C38004DCC000,"The Flame In The Flood: Complete Edition",gpu;nvdec;UE4,ingame,2022-08-22 16:23:49
010007700D4AC000,"The Forbidden Arts",,playable,2021-01-26 16:26:24
010030700CBBC000,"The friends of Ringo Ishikawa",,playable,2022-08-22 16:33:17
0100b620139d8000,"The Game of Life 2",ldn-untested,ingame,2025-02-03 22:30:00
01006350148DA000,"The Gardener and the Wild Vines",gpu,ingame,2024-04-29 16:32:10
0100B13007A6A000,"The Gardens Between",,playable,2021-01-29 16:16:53
010036E00FB20000,"The Great Ace Attorney Chronicles",,playable,2023-06-22 21:26:29
@@ -2995,8 +2981,6 @@
010015D003EE4000,"The Jackbox Party Pack 2",online-working,playable,2022-08-22 18:23:40
0100CC80013D6000,"The Jackbox Party Pack 3",slow;online-working,playable,2022-08-22 18:41:06
0100E1F003EE8000,"The Jackbox Party Pack 4",online-working,playable,2022-08-22 18:56:34
01006fe0096ac000,"The Jackbox Party Pack 5",slow;online-working,ingame,2025-02-14 05:32:00
01005a400db52000,"The Jackbox Party Pack 6",slow;online-working,ingame,2025-02-14 05:26:00
010052C00B184000,"The Journey Down: Chapter One",nvdec,playable,2021-02-24 13:32:41
01006BC00B188000,"The Journey Down: Chapter Three",nvdec,playable,2021-02-24 13:45:27
01009AB00B186000,"The Journey Down: Chapter Two",nvdec,playable,2021-02-24 13:32:13
@@ -3175,7 +3159,6 @@
010055E00CA68000,"Trine 4: The Nightmare Prince",gpu,nothing,2025-01-07 05:47:46
0100D9000A930000,"Trine Enchanted Edition",ldn-untested;nvdec,playable,2021-06-03 11:28:15
01002D7010A54000,"Trinity Trigger",crash,ingame,2023-03-03 03:09:09
010020700a5e0000,"TRIVIAL PURSUIT Live!",ldn-untested,ingame,2025-02-03 22:35:00
0100868013FFC000,"TRIVIAL PURSUIT Live! 2",,boots,2022-12-19 00:04:33
0100F78002040000,"Troll and I™",gpu;nvdec,ingame,2021-06-04 16:58:50
0100145011008000,"Trollhunters: Defenders of Arcadia",gpu;nvdec,ingame,2020-11-30 13:27:09
@@ -3225,7 +3208,6 @@
0100AB2010B4C000,"Unlock The King",,playable,2020-09-01 13:58:27
0100A3E011CB0000,"Unlock the King 2",,playable,2021-06-15 20:43:55
01005AA00372A000,"UNO® for Nintendo Switch",nvdec;ldn-untested,playable,2022-07-28 14:49:47
0100b6e012ebe000,"UNO",ldn-untested,ingame,2025-02-03 22:40:00
0100E5D00CC0C000,"Unravel Two",nvdec,playable,2024-05-23 15:45:05
010001300CC4A000,"Unruly Heroes",,playable,2021-01-07 18:09:31
0100B410138C0000,"Unspottable",,playable,2022-10-25 19:28:49
@@ -3390,7 +3372,6 @@
0100F47016F26000,"Yomawari 3",,playable,2022-05-10 08:26:51
010012F00B6F2000,"Yomawari: The Long Night Collection",,playable,2022-09-03 14:36:59
0100CC600ABB2000,"Yonder: The Cloud Catcher Chronicles (Retail Only)",,playable,2021-01-28 14:06:25
0100534009ff2000,"Yonder: The Cloud Catcher Chronicles",,playable,2025-02-03 22:19:13
0100BE50042F6000,"Yono and the Celestial Elephants",,playable,2021-01-28 18:23:58
0100F110029C8000,"Yooka-Laylee",,playable,2021-01-28 14:21:45
010022F00DA66000,"Yooka-Laylee and the Impossible Lair",,playable,2021-03-05 17:32:21
1 title_id game_name labels status last_updated
332 0100E680149DC000 Arcaea playable 2023-03-16 19:31:21
333 01003C2010C78000 Archaica: The Path Of Light crash nothing 2020-10-16 13:22:26
334 01004DA012976000 Area 86 playable 2020-12-16 16:45:52
01008d8006a6a000 Arena of Valor crash boots 2025-02-03 22:19:34
335 0100691013C46000 ARIA CHRONICLE playable 2022-11-16 13:50:55
336 0100D4A00B284000 ARK: Survival Evolved gpu;nvdec;online-broken;UE4;ldn-untested ingame 2024-04-16 00:53:56
337 0100C56012C96000 Arkanoid vs. Space Invaders services ingame 2021-01-21 12:50:30
426 0100E48013A34000 Balan Wonderworld Demo gpu;services;UE4;demo ingame 2023-02-16 20:05:07
427 0100CD801CE5E000 Balatro ingame 2024-04-21 02:01:53
428 010010A00DA48000 Baldur's Gate and Baldur's Gate II: Enhanced Editions 32-bit playable 2022-09-12 23:52:15
0100fd1014726000 Baldur's Gate: Dark Alliance ldn-untested ingame 2025-02-03 22:21:00
429 0100BC400FB64000 Balthazar's Dream playable 2022-09-13 00:13:22
430 01008D30128E0000 Bamerang playable 2022-10-26 00:29:39
431 010013C010C5C000 Banner of the Maid playable 2021-06-14 15:23:37
528 01005950022EC000 Blade Strangers nvdec playable 2022-07-17 19:02:43
529 0100DF0011A6A000 Bladed Fury playable 2022-10-26 11:36:26
530 0100CFA00CC74000 Blades of Time deadlock;online boots 2022-07-17 19:19:58
01003d700dd8a000 Blades boots 2025-02-03 22:22:00
531 01006CC01182C000 Blair Witch nvdec;UE4 playable 2022-10-01 14:06:16
532 010039501405E000 Blanc gpu;slow ingame 2023-02-22 14:00:13
533 0100698009C6E000 Blasphemous nvdec playable 2021-03-01 12:15:31
628 010030D012FF6000 Bus Driver Simulator playable 2022-10-17 13:55:27
629 0100A9101418C000 BUSTAFELLOWS nvdec playable 2020-10-17 20:04:41
630 0100177005C8A000 BUTCHER playable 2021-01-11 18:50:17
01008c2019598000 Bluey: The Videogame playable 2025-02-11 04:38:00
631 01000B900D8B0000 Cadence of Hyrule: Crypt of the NecroDancer Featuring The Legend of Zelda slow;nvdec playable 2024-04-01 22:43:40
632 010065700EE06000 Cadence of Hyrule: Crypt of the NecroDancer Featuring The Legend of Zelda Demo demo;gpu;nvdec ingame 2021-02-14 21:48:15
633 01005C00117A8000 Café Enchanté playable 2020-11-13 14:54:25
955 010012800EBAE000 Disney TSUM TSUM FESTIVAL crash menus 2020-07-14 14:05:28
956 01009740120FE000 DISTRAINT 2 playable 2020-09-03 16:08:12
957 010075B004DD2000 DISTRAINT: Deluxe Edition playable 2020-06-15 23:42:24
958 010027400CDC6000 Divinity: Original Sin 2 - Definitive Edition services;crash;online-broken;regression ingame menus 2025-02-03 22:12:30 2023-08-13 17:20:03
959 01001770115C8000 Dodo Peak nvdec;UE4 playable 2022-10-04 16:13:05
960 010077B0100DA000 Dogurai playable 2020-10-04 02:40:16
961 010048100D51A000 Dokapon Up! Mugen no Roulette gpu;Needs Update menus 2022-12-08 19:39:10
966 0100751007ADA000 Don't Starve: Nintendo Switch Edition nvdec playable 2022-02-05 20:43:34
967 010088B010DD2000 Dongo Adventure playable 2022-10-04 16:22:26
968 0100C1F0051B6000 Donkey Kong Country™: Tropical Freeze playable 2024-08-05 16:46:10
01009D901BC56000 Donkey Kong Country™: Returns HD gpu ingame 2025-02-16 13:44:12
969 0100F2C00F060000 Doodle Derby boots 2020-12-04 22:51:48
970 0100416004C00000 DOOM gpu;slow;nvdec;online-broken ingame 2024-09-23 15:40:07
971 010018900DD00000 DOOM (1993) nvdec;online-broken menus 2022-09-06 13:32:19
1155 010095600AA36000 Fill-a-Pix: Phil's Epic Adventure playable 2020-12-22 13:48:22
1156 0100C3A00BB76000 Fimbul nvdec playable 2022-07-26 13:31:47
1157 0100C8200E942000 Fin and the Ancient Mystery nvdec playable 2020-12-17 16:40:39
1158 01000EA014150000 FINAL FANTASY crash playable nothing 2025-02-16 21:27:30 2024-09-05 20:55:30
1159 01006B7014156000 FINAL FANTASY II crash nothing 2024-04-13 19:18:04
1160 01006F000B056000 FINAL FANTASY IX audout;nvdec playable 2021-06-05 11:35:00
1161 0100AA201415C000 FINAL FANTASY V playable 2023-04-26 01:11:55
1246 0100A6B00D4EC000 Furwind playable 2021-02-19 19:44:08
1247 0100ECE00C0C4000 Fury Unleashed crash;services ingame 2020-10-18 11:52:40
1248 010070000ED9E000 Fury Unleashed Demo playable 2020-10-08 20:09:21
1249 0100E1F013674000 FUSER™ nvdec;UE4;slow;gpu nvdec;UE4 ingame playable 2025-02-12 16:03:00 2022-10-17 20:58:32
1250 0100A7A015E4C000 Fushigi no Gensokyo Lotus Labyrinth Needs Update;audio;gpu;nvdec ingame 2021-01-20 15:30:02
1251 01003C300B274000 Futari de! Nyanko Daisensou playable 2024-01-05 22:26:52
1252 010055801134E000 FUZE Player online-broken;vulkan-backend-bug ingame 2022-10-18 12:23:53
1378 0100763015C2E000 Gunvolt Chronicles: Luminous Avenger iX 2 crash;Needs Update nothing 2022-04-29 15:34:34
1379 01002C8018554000 Gurimugurimoa OnceMore Demo playable 2022-07-29 22:07:31
1380 0100AC601DCA8000 GYLT crash ingame 2024-03-18 20:16:51
0100c3c012718000 Grand Theft Auto: III – The Definitive Edition gpu;UE4 ingame 2022-10-31 20:13:52
0100182014022000 Grand Theft Auto: Vice City – The Definitive Edition gpu;UE4 ingame 2022-10-31 20:13:52
010065a014024000 Grand Theft Auto: San Andreas – The Definitive Edition gpu;UE4 ingame 2022-10-31 20:13:52
1381 0100822012D76000 HAAK gpu ingame 2023-02-19 14:31:05
1382 01007E100EFA8000 Habroxia playable 2020-06-16 23:04:42
1383 0100535012974000 Hades vulkan playable 2022-10-05 10:45:21
1432 010083A018262000 Hitman: Blood Money — Reprisal deadlock ingame 2024-09-28 16:28:50
1433 01004B100A5CC000 Hob: The Definitive Edition playable 2021-01-13 09:39:19
1434 0100F7300ED2C000 Hoggy2 playable 2022-10-10 13:53:35
1435 0100F7E00C70E000 Hogwarts Legacy UE4;slow slow ingame 2024-09-03 19:53:58
1436 0100633007D48000 Hollow Knight nvdec playable 2023-01-16 15:44:56
1437 0100F2100061E800 Hollow0 UE4;gpu ingame 2021-03-03 23:42:56
1438 0100342009E16000 Holy Potatoes! What The Hell?! playable 2020-07-03 10:48:56
1654 0100A73006E74000 Legendary Eleven playable 2021-06-08 12:09:03
1655 0100A7700B46C000 Legendary Fishing online playable 2021-04-14 15:08:46
1656 0100739018020000 LEGO® 2K Drive gpu;ldn-works ingame 2024-04-09 02:05:12
1657 010085500130a000 01003A30012C0000 LEGO® CITY Undercover nvdec playable 2024-09-30 08:44:27
1658 010070D009FEC000 LEGO® DC Super-Villains playable 2021-05-27 18:10:37
1659 010052A00B5D2000 LEGO® Harry Potter™ Collection crash ingame 2024-01-31 10:28:07
1660 010073C01AF34000 LEGO® Horizon Adventures™ vulkan-backend-bug;opengl-backend-bug;UE4 ingame 2025-01-07 04:24:56
1796 010005A00B312000 Megaton Rainfall gpu;opengl boots 2022-08-04 18:29:43
1797 0100EA100DF92000 Meiji Katsugeki Haikara Ryuuseigumi - Seibai Shimaseu, Yonaoshi Kagyou 32-bit;nvdec playable 2022-12-05 13:19:12
1798 0100B360068B2000 Mekorama gpu boots 2021-06-17 16:37:21
010012301932A000 Melatonin playable 2025-02-16 04:08:17
1799 01000FA010340000 Melbits World nvdec;online menus 2021-11-26 13:51:22
1800 0100F68019636000 Melon Journey playable 2023-04-23 21:20:01
1801 010079C012896000 Memories Off -Innocent Fille- for Dearest playable 2020-08-04 07:31:22
1913 010073E008E6E000 Mugsters playable 2021-01-28 17:57:17
1914 0100A8400471A000 MUJO playable 2020-05-08 16:31:04
1915 0100211005E94000 Mulaka playable 2021-01-28 18:07:20
01008e2013fb4000 Multi Quiz ldn-untested ingame 2025-02-03 22:26:00
1916 010038B00B9AE000 Mummy Pinball playable 2022-08-05 16:08:11
1917 01008E200C5C2000 Muse Dash playable 2020-06-06 14:41:29
1918 010035901046C000 Mushroom Quest playable 2020-05-17 13:07:08
2028 010003C00B868000 Ninjin: Clash of Carrots online-broken playable 2024-07-10 05:12:26
2029 0100746010E4C000 NinNinDays playable 2022-11-20 15:17:29
2030 0100C9A00ECE6000 Nintendo 64™ – Nintendo Switch Online gpu;vulkan ingame 2024-04-23 20:21:07
0100e0601c632000 Nintendo 64™ – Nintendo Switch Online: MATURE 17+ ingame 2025-02-03 22:27:00
2031 0100D870045B6000 Nintendo Entertainment System™ - Nintendo Switch Online online playable 2022-07-01 15:45:06
2032 0100C4B0034B2000 Nintendo Labo Toy-Con 01 Variety Kit gpu ingame 2022-08-07 12:56:07
2033 01001E9003502000 Nintendo Labo Toy-Con 03 Vehicle Kit services;crash menus 2022-08-03 17:20:11
2058 010002700C34C000 Numbala playable 2020-05-11 12:01:07
2059 010020500C8C8000 Number Place 10000 gpu menus 2021-11-24 09:14:23
2060 010003701002C000 Nurse Love Syndrome playable 2022-10-13 10:05:22
2061 0000000000000000 nx-hbmenu Needs Update;homebrew boots 2024-04-06 22:05:32
2062 nxquake2 services;crash;homebrew nothing 2022-08-04 23:14:04
2063 010049F00EC30000 Nyan Cat: Lost in Space online playable 2021-06-12 13:22:03
2064 01002E6014FC4000 O---O playable 2022-10-29 12:12:14
2466 0100AFE00DDAC000 Royal Roads playable 2020-11-17 12:54:38
2467 0100E2C00B414000 RPG Maker MV nvdec playable 2021-01-05 20:12:01
2468 01005CD015986000 rRootage Reloaded playable 2022-08-05 23:20:18
2469 0000000000000000 RSDKv5u homebrew ingame 2024-04-01 16:25:34
2470 010009B00D33C000 Rugby Challenge 4 slow;online-broken;UE4 playable 2022-10-06 12:45:53
2471 01006EC00F2CC000 RUINER UE4 playable 2022-10-03 14:11:33
2472 010074F00DE4A000 Run the Fan playable 2021-02-27 13:36:28
2475 010081C0191D8000 Rune Factory 3 Special playable 2023-10-15 08:32:49
2476 010051D00E3A4000 Rune Factory 4 Special 32-bit;crash;nvdec ingame 2023-05-06 08:49:17
2477 010014D01216E000 Rune Factory 5 (JP) gpu ingame 2021-06-01 12:00:36
010071E0145F8000 Rustler playable 2025-02-10 20:17:12
2478 0100E21013908000 RWBY: Grimm Eclipse - Definitive Edition online-broken playable 2022-11-03 10:44:01
2479 010012C0060F0000 RXN -Raijin- nvdec playable 2021-01-10 16:05:43
2480 0100B8B012ECA000 S.N.I.P.E.R. - Hunter Scope playable 2021-04-19 15:58:09
2532 0100C3E00B700000 SEGA AGES Space Harrier playable 2021-01-11 12:57:40
2533 010054400D2E6000 SEGA AGES Virtua Racing online-broken playable 2023-01-29 17:08:39
2534 01001E700AC60000 SEGA AGES Wonder Boy: Monster Land online playable 2021-05-05 16:28:25
2535 0100B3C014BDA000 SEGA Genesis™ – Nintendo Switch Online crash;regression ingame nothing 2025-02-03 22:13:30 2022-04-11 07:27:21
2536 0100F7300B24E000 SEGA Mega Drive Classics online playable 2021-01-05 11:08:00
2537 01009840046BC000 Semispheres playable 2021-01-06 23:08:31
2538 0100D1800D902000 SENRAN KAGURA Peach Ball playable 2021-06-03 15:12:10
2668 01004F401BEBE000 Song of Nunu: A League of Legends Story ingame 2024-07-12 18:53:44
2669 0100E5400BF94000 Songbird Symphony playable 2021-02-27 02:44:04
2670 010031D00A604000 Songbringer playable 2020-06-22 10:42:02
2671 0000000000000000 Sonic 1 (2013) crash;homebrew ingame 2024-04-06 18:31:20
2672 0000000000000000 Sonic 2 (2013) crash;homebrew ingame 2024-04-01 16:25:30
2673 0000000000000000 Sonic A.I.R homebrew ingame 2024-04-01 16:25:32
2674 0000000000000000 Sonic CD crash;homebrew ingame 2024-04-01 16:25:31
2675 010040E0116B8000 Sonic Colors: Ultimate playable 2022-11-12 21:24:26
2676 01001270012B6000 SONIC FORCES™ playable 2024-07-28 13:11:21
2677 01004AD014BF0000 Sonic Frontiers gpu;deadlock;amd-vendor-bug;intel-vendor-bug ingame 2024-09-05 09:18:53
2688 0100707011722000 Space Elite Force playable 2020-11-27 15:21:05
2689 010047B010260000 Space Pioneer playable 2022-10-20 12:24:37
2690 010010A009830000 Space Ribbon playable 2022-08-15 17:17:10
2691 0000000000000000 SpaceCadetPinball homebrew ingame 2024-04-18 19:30:04
2692 0100D9B0041CE000 Spacecats with Lasers playable 2022-08-15 17:22:44
2693 010034800FB60000 Spaceland playable 2020-11-01 14:31:56
2694 010028D0045CE000 Sparkle 2 playable 2020-10-19 11:51:39
2721 0100C2500FC20000 Splatoon™ 3 ldn-works;opengl-backend-bug;LAN;amd-vendor-bug playable 2024-08-04 23:49:11
2722 0100BA0018500000 Splatoon™ 3: Splatfest World Premiere gpu;online-broken;demo ingame 2022-09-19 03:17:12
2723 010062800D39C000 SpongeBob SquarePants: Battle for Bikini Bottom - Rehydrated online-broken;UE4;ldn-broken;vulkan-backend-bug playable 2023-08-01 19:29:34
2724 01009FB0172F4000 SpongeBob SquarePants: The Cosmic Shake gpu;UE4 ingame 2024-03-04 16:35:00 2023-08-01 19:29:53
2725 010097C01336A000 Spooky Chase playable 2022-11-04 12:17:44
2726 0100C6100D75E000 Spooky Ghosts Dot Com playable 2021-06-15 15:16:11
2727 0100DE9005170000 Sports Party nvdec playable 2021-03-05 13:40:42
2832 01009B90006DC000 Super Mario Maker™ 2 online-broken;ldn-broken playable 2024-08-25 11:05:19
2833 0100000000010000 Super Mario Odyssey™ nvdec;intel-vendor-bug;mac-bug playable 2024-08-25 01:32:34
2834 010036B0034E4000 Super Mario Party™ gpu;Needs Update;ldn-works ingame 2024-06-21 05:10:16
0100965017338000 Super Mario Party Jamboree mac-bug;gpu ingame 2025-02-17 02:09:20
2835 0100BC0018138000 Super Mario RPG™ gpu;audio;nvdec ingame 2024-06-19 17:43:42
2836 0000000000000000 Super Mario World homebrew boots 2024-06-13 01:40:31
2837 010049900F546000 Super Mario™ 3D All-Stars services-horizon;slow;vulkan;amd-vendor-bug ingame 2024-05-07 02:38:16
2838 010028600EBDA000 Super Mario™ 3D World + Bowser’s Fury ldn-works playable 2024-07-31 10:45:37
2839 01004F8006A78000 Super Meat Boy services playable 2020-04-02 23:10:07
2964 0100C38004DCC000 The Flame In The Flood: Complete Edition gpu;nvdec;UE4 ingame 2022-08-22 16:23:49
2965 010007700D4AC000 The Forbidden Arts playable 2021-01-26 16:26:24
2966 010030700CBBC000 The friends of Ringo Ishikawa playable 2022-08-22 16:33:17
0100b620139d8000 The Game of Life 2 ldn-untested ingame 2025-02-03 22:30:00
2967 01006350148DA000 The Gardener and the Wild Vines gpu ingame 2024-04-29 16:32:10
2968 0100B13007A6A000 The Gardens Between playable 2021-01-29 16:16:53
2969 010036E00FB20000 The Great Ace Attorney Chronicles playable 2023-06-22 21:26:29
2981 010015D003EE4000 The Jackbox Party Pack 2 online-working playable 2022-08-22 18:23:40
2982 0100CC80013D6000 The Jackbox Party Pack 3 slow;online-working playable 2022-08-22 18:41:06
2983 0100E1F003EE8000 The Jackbox Party Pack 4 online-working playable 2022-08-22 18:56:34
01006fe0096ac000 The Jackbox Party Pack 5 slow;online-working ingame 2025-02-14 05:32:00
01005a400db52000 The Jackbox Party Pack 6 slow;online-working ingame 2025-02-14 05:26:00
2984 010052C00B184000 The Journey Down: Chapter One nvdec playable 2021-02-24 13:32:41
2985 01006BC00B188000 The Journey Down: Chapter Three nvdec playable 2021-02-24 13:45:27
2986 01009AB00B186000 The Journey Down: Chapter Two nvdec playable 2021-02-24 13:32:13
3159 010055E00CA68000 Trine 4: The Nightmare Prince gpu nothing 2025-01-07 05:47:46
3160 0100D9000A930000 Trine Enchanted Edition ldn-untested;nvdec playable 2021-06-03 11:28:15
3161 01002D7010A54000 Trinity Trigger crash ingame 2023-03-03 03:09:09
010020700a5e0000 TRIVIAL PURSUIT Live! ldn-untested ingame 2025-02-03 22:35:00
3162 0100868013FFC000 TRIVIAL PURSUIT Live! 2 boots 2022-12-19 00:04:33
3163 0100F78002040000 Troll and I™ gpu;nvdec ingame 2021-06-04 16:58:50
3164 0100145011008000 Trollhunters: Defenders of Arcadia gpu;nvdec ingame 2020-11-30 13:27:09
3208 0100AB2010B4C000 Unlock The King playable 2020-09-01 13:58:27
3209 0100A3E011CB0000 Unlock the King 2 playable 2021-06-15 20:43:55
3210 01005AA00372A000 UNO® for Nintendo Switch nvdec;ldn-untested playable 2022-07-28 14:49:47
0100b6e012ebe000 UNO ldn-untested ingame 2025-02-03 22:40:00
3211 0100E5D00CC0C000 Unravel Two nvdec playable 2024-05-23 15:45:05
3212 010001300CC4A000 Unruly Heroes playable 2021-01-07 18:09:31
3213 0100B410138C0000 Unspottable playable 2022-10-25 19:28:49
3372 0100F47016F26000 Yomawari 3 playable 2022-05-10 08:26:51
3373 010012F00B6F2000 Yomawari: The Long Night Collection playable 2022-09-03 14:36:59
3374 0100CC600ABB2000 Yonder: The Cloud Catcher Chronicles (Retail Only) playable 2021-01-28 14:06:25
0100534009ff2000 Yonder: The Cloud Catcher Chronicles playable 2025-02-03 22:19:13
3375 0100BE50042F6000 Yono and the Celestial Elephants playable 2021-01-28 18:23:58
3376 0100F110029C8000 Yooka-Laylee playable 2021-01-28 14:21:45
3377 010022F00DA66000 Yooka-Laylee and the Impossible Lair playable 2021-03-05 17:32:21

View File

@@ -3,7 +3,6 @@
<PropertyGroup>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
<Configurations>Debug;Release;DebugAOT;ReleaseAOT</Configurations>
</PropertyGroup>
<ItemGroup>

View File

@@ -7,17 +7,15 @@ namespace ARMeilleure.Instructions
{
static partial class InstEmit
{
private const string SupervisorCallName = nameof(NativeInterface.SupervisorCall);
private const string BreakName = nameof(NativeInterface.Break);
private const string UndefinedName = nameof(NativeInterface.Undefined);
public static void Brk(ArmEmitterContext context)
{
OpCodeException op = (OpCodeException)context.CurrOp;
string name = nameof(NativeInterface.Break);
context.StoreToContext();
context.Call(NativeInterface.Type.GetMethod(BreakName), Const(op.Address), Const(op.Id));
context.Call(typeof(NativeInterface).GetMethod(name), Const(op.Address), Const(op.Id));
context.LoadFromContext();
@@ -27,10 +25,12 @@ namespace ARMeilleure.Instructions
public static void Svc(ArmEmitterContext context)
{
OpCodeException op = (OpCodeException)context.CurrOp;
string name = nameof(NativeInterface.SupervisorCall);
context.StoreToContext();
context.Call(NativeInterface.Type.GetMethod(SupervisorCallName), Const(op.Address), Const(op.Id));
context.Call(typeof(NativeInterface).GetMethod(name), Const(op.Address), Const(op.Id));
context.LoadFromContext();
@@ -41,9 +41,11 @@ namespace ARMeilleure.Instructions
{
OpCode op = context.CurrOp;
string name = nameof(NativeInterface.Undefined);
context.StoreToContext();
context.Call(NativeInterface.Type.GetMethod(UndefinedName), Const(op.Address), Const(op.RawOpCode));
context.Call(typeof(NativeInterface).GetMethod(name), Const(op.Address), Const(op.RawOpCode));
context.LoadFromContext();

View File

@@ -14,7 +14,7 @@ namespace ARMeilleure.Instructions
context.StoreToContext();
context.Call(NativeInterface.Type.GetMethod(name), Const(((IOpCode)op).Address), Const(op.Id));
context.Call(typeof(NativeInterface).GetMethod(name), Const(((IOpCode)op).Address), Const(op.Id));
context.LoadFromContext();
@@ -29,7 +29,7 @@ namespace ARMeilleure.Instructions
context.StoreToContext();
context.Call(NativeInterface.Type.GetMethod(name), Const(((IOpCode)op).Address), Const(op.Id));
context.Call(typeof(NativeInterface).GetMethod(name), Const(((IOpCode)op).Address), Const(op.Id));
context.LoadFromContext();

View File

@@ -478,7 +478,7 @@ namespace ARMeilleure.Instructions
context.BranchIf(lblNotWatched, pte, Const(0L), Comparison.GreaterOrEqual, BasicBlockFrequency.Cold);
// Signal memory tracking. Size here doesn't matter as address is assumed to be size aligned here.
context.Call(NativeInterface.Type.GetMethod(nameof(NativeInterface.SignalMemoryTracking)), address, Const(1UL), Const(write ? 1 : 0));
context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SignalMemoryTracking)), address, Const(1UL), Const(write ? 1 : 0));
context.MarkLabel(lblNotWatched);
pte = context.BitwiseAnd(pte, Const(0xffffffffffffUL)); // Ignore any software protection bits. (they are still used by C# memory access)
@@ -489,7 +489,7 @@ namespace ARMeilleure.Instructions
context.BranchIfTrue(lblNonNull, pte, BasicBlockFrequency.Cold);
// The call is not expected to return (it should throw).
context.Call(NativeInterface.Type.GetMethod(nameof(NativeInterface.ThrowInvalidMemoryAccess)), address);
context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ThrowInvalidMemoryAccess)), address);
context.MarkLabel(lblNonNull);
}
@@ -535,16 +535,16 @@ namespace ARMeilleure.Instructions
switch (size)
{
case 0:
info = NativeInterface.Type.GetMethod(nameof(NativeInterface.ReadByte));
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadByte));
break;
case 1:
info = NativeInterface.Type.GetMethod(nameof(NativeInterface.ReadUInt16));
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt16));
break;
case 2:
info = NativeInterface.Type.GetMethod(nameof(NativeInterface.ReadUInt32));
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt32));
break;
case 3:
info = NativeInterface.Type.GetMethod(nameof(NativeInterface.ReadUInt64));
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt64));
break;
}
@@ -564,19 +564,19 @@ namespace ARMeilleure.Instructions
switch (size)
{
case 0:
info = NativeInterface.Type.GetMethod(nameof(NativeInterface.ReadByte));
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadByte));
break;
case 1:
info = NativeInterface.Type.GetMethod(nameof(NativeInterface.ReadUInt16));
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt16));
break;
case 2:
info = NativeInterface.Type.GetMethod(nameof(NativeInterface.ReadUInt32));
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt32));
break;
case 3:
info = NativeInterface.Type.GetMethod(nameof(NativeInterface.ReadUInt64));
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt64));
break;
case 4:
info = NativeInterface.Type.GetMethod(nameof(NativeInterface.ReadVector128));
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadVector128));
break;
}
@@ -608,16 +608,16 @@ namespace ARMeilleure.Instructions
switch (size)
{
case 0:
info = NativeInterface.Type.GetMethod(nameof(NativeInterface.WriteByte));
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteByte));
break;
case 1:
info = NativeInterface.Type.GetMethod(nameof(NativeInterface.WriteUInt16));
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt16));
break;
case 2:
info = NativeInterface.Type.GetMethod(nameof(NativeInterface.WriteUInt32));
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt32));
break;
case 3:
info = NativeInterface.Type.GetMethod(nameof(NativeInterface.WriteUInt64));
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt64));
break;
}
@@ -643,19 +643,19 @@ namespace ARMeilleure.Instructions
switch (size)
{
case 0:
info = NativeInterface.Type.GetMethod(nameof(NativeInterface.WriteByte));
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteByte));
break;
case 1:
info = NativeInterface.Type.GetMethod(nameof(NativeInterface.WriteUInt16));
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt16));
break;
case 2:
info = NativeInterface.Type.GetMethod(nameof(NativeInterface.WriteUInt32));
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt32));
break;
case 3:
info = NativeInterface.Type.GetMethod(nameof(NativeInterface.WriteUInt64));
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt64));
break;
case 4:
info = NativeInterface.Type.GetMethod(nameof(NativeInterface.WriteVector128));
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteVector128));
break;
}

View File

@@ -406,7 +406,7 @@ namespace ARMeilleure.Instructions
{
Operand res = EmitSoftFloatCall(context, nameof(SoftFloat32.FPSub), op1, op2);
return EmitUnaryMathCall(context, nameof(Math.Abs), res);
return EmitUnaryMathCall(context, nameof(MathHelper.Abs), res);
});
}
}
@@ -451,7 +451,7 @@ namespace ARMeilleure.Instructions
{
Operand res = EmitSoftFloatCall(context, nameof(SoftFloat32.FPSub), op1, op2);
return EmitUnaryMathCall(context, nameof(Math.Abs), res);
return EmitUnaryMathCall(context, nameof(MathHelper.Abs), res);
});
}
}
@@ -483,7 +483,7 @@ namespace ARMeilleure.Instructions
{
EmitScalarUnaryOpF(context, (op1) =>
{
return EmitUnaryMathCall(context, nameof(Math.Abs), op1);
return EmitUnaryMathCall(context, nameof(MathHelper.Abs), op1);
});
}
}
@@ -522,7 +522,7 @@ namespace ARMeilleure.Instructions
{
EmitVectorUnaryOpF(context, (op1) =>
{
return EmitUnaryMathCall(context, nameof(Math.Abs), op1);
return EmitUnaryMathCall(context, nameof(MathHelper.Abs), op1);
});
}
}
@@ -2246,7 +2246,7 @@ namespace ARMeilleure.Instructions
{
EmitScalarUnaryOpF(context, (op1) =>
{
return EmitUnaryMathCall(context, nameof(Math.Floor), op1);
return EmitUnaryMathCall(context, nameof(MathHelper.Floor), op1);
});
}
}
@@ -2265,7 +2265,7 @@ namespace ARMeilleure.Instructions
{
EmitVectorUnaryOpF(context, (op1) =>
{
return EmitUnaryMathCall(context, nameof(Math.Floor), op1);
return EmitUnaryMathCall(context, nameof(MathHelper.Floor), op1);
});
}
}
@@ -2322,7 +2322,7 @@ namespace ARMeilleure.Instructions
{
EmitScalarUnaryOpF(context, (op1) =>
{
return EmitUnaryMathCall(context, nameof(Math.Ceiling), op1);
return EmitUnaryMathCall(context, nameof(MathHelper.Ceiling), op1);
});
}
}
@@ -2341,7 +2341,7 @@ namespace ARMeilleure.Instructions
{
EmitVectorUnaryOpF(context, (op1) =>
{
return EmitUnaryMathCall(context, nameof(Math.Ceiling), op1);
return EmitUnaryMathCall(context, nameof(MathHelper.Ceiling), op1);
});
}
}
@@ -2390,7 +2390,7 @@ namespace ARMeilleure.Instructions
{
EmitScalarUnaryOpF(context, (op1) =>
{
return EmitUnaryMathCall(context, nameof(Math.Truncate), op1);
return EmitUnaryMathCall(context, nameof(MathHelper.Truncate), op1);
});
}
}
@@ -2409,7 +2409,7 @@ namespace ARMeilleure.Instructions
{
EmitVectorUnaryOpF(context, (op1) =>
{
return EmitUnaryMathCall(context, nameof(Math.Truncate), op1);
return EmitUnaryMathCall(context, nameof(MathHelper.Truncate), op1);
});
}
}

View File

@@ -43,7 +43,7 @@ namespace ARMeilleure.Instructions
}
else
{
EmitScalarUnaryOpF32(context, (op1) => EmitUnaryMathCall(context, nameof(Math.Abs), op1));
EmitScalarUnaryOpF32(context, (op1) => EmitUnaryMathCall(context, nameof(MathHelper.Abs), op1));
}
}
@@ -66,7 +66,7 @@ namespace ARMeilleure.Instructions
}
else
{
EmitVectorUnaryOpF32(context, (op1) => EmitUnaryMathCall(context, nameof(Math.Abs), op1));
EmitVectorUnaryOpF32(context, (op1) => EmitUnaryMathCall(context, nameof(MathHelper.Abs), op1));
}
}
else

View File

@@ -726,8 +726,8 @@ namespace ARMeilleure.Instructions
if (absolute)
{
ne = EmitUnaryMathCall(context, nameof(Math.Abs), ne);
me = EmitUnaryMathCall(context, nameof(Math.Abs), me);
ne = EmitUnaryMathCall(context, nameof(MathHelper.Abs), ne);
me = EmitUnaryMathCall(context, nameof(MathHelper.Abs), me);
}
Operand e = EmitSoftFloatCall(context, name, ne, me);

View File

@@ -333,7 +333,7 @@ namespace ARMeilleure.Instructions
}
else
{
EmitFcvt_s_Gp(context, (op1) => EmitUnaryMathCall(context, nameof(Math.Floor), op1));
EmitFcvt_s_Gp(context, (op1) => EmitUnaryMathCall(context, nameof(MathHelper.Floor), op1));
}
}
@@ -349,7 +349,7 @@ namespace ARMeilleure.Instructions
}
else
{
EmitFcvt(context, (op1) => EmitUnaryMathCall(context, nameof(Math.Floor), op1), signed: true, scalar: false);
EmitFcvt(context, (op1) => EmitUnaryMathCall(context, nameof(MathHelper.Floor), op1), signed: true, scalar: false);
}
}
@@ -365,7 +365,7 @@ namespace ARMeilleure.Instructions
}
else
{
EmitFcvt_u_Gp(context, (op1) => EmitUnaryMathCall(context, nameof(Math.Floor), op1));
EmitFcvt_u_Gp(context, (op1) => EmitUnaryMathCall(context, nameof(MathHelper.Floor), op1));
}
}
@@ -538,7 +538,7 @@ namespace ARMeilleure.Instructions
}
else
{
EmitFcvt_s_Gp(context, (op1) => EmitUnaryMathCall(context, nameof(Math.Ceiling), op1));
EmitFcvt_s_Gp(context, (op1) => EmitUnaryMathCall(context, nameof(MathHelper.Ceiling), op1));
}
}
@@ -554,7 +554,7 @@ namespace ARMeilleure.Instructions
}
else
{
EmitFcvt_u_Gp(context, (op1) => EmitUnaryMathCall(context, nameof(Math.Ceiling), op1));
EmitFcvt_u_Gp(context, (op1) => EmitUnaryMathCall(context, nameof(MathHelper.Ceiling), op1));
}
}

View File

@@ -357,10 +357,10 @@ namespace ARMeilleure.Instructions
toConvert = EmitRoundMathCall(context, MidpointRounding.ToEven, toConvert);
break;
case 0b10: // Towards positive infinity
toConvert = EmitUnaryMathCall(context, nameof(Math.Ceiling), toConvert);
toConvert = EmitUnaryMathCall(context, nameof(MathHelper.Ceiling), toConvert);
break;
case 0b11: // Towards negative infinity
toConvert = EmitUnaryMathCall(context, nameof(Math.Floor), toConvert);
toConvert = EmitUnaryMathCall(context, nameof(MathHelper.Floor), toConvert);
break;
}
@@ -494,10 +494,10 @@ namespace ARMeilleure.Instructions
toConvert = EmitRoundMathCall(context, MidpointRounding.ToEven, toConvert);
break;
case 0b10: // Towards positive infinity
toConvert = EmitUnaryMathCall(context, nameof(Math.Ceiling), toConvert);
toConvert = EmitUnaryMathCall(context, nameof(MathHelper.Ceiling), toConvert);
break;
case 0b11: // Towards negative infinity
toConvert = EmitUnaryMathCall(context, nameof(Math.Floor), toConvert);
toConvert = EmitUnaryMathCall(context, nameof(MathHelper.Floor), toConvert);
break;
}
@@ -534,7 +534,7 @@ namespace ARMeilleure.Instructions
}
else
{
EmitVectorUnaryOpF32(context, (m) => EmitUnaryMathCall(context, nameof(Math.Floor), m));
EmitVectorUnaryOpF32(context, (m) => EmitUnaryMathCall(context, nameof(MathHelper.Floor), m));
}
}
@@ -574,7 +574,7 @@ namespace ARMeilleure.Instructions
}
else
{
EmitVectorUnaryOpF32(context, (m) => EmitUnaryMathCall(context, nameof(Math.Ceiling), m));
EmitVectorUnaryOpF32(context, (m) => EmitUnaryMathCall(context, nameof(MathHelper.Ceiling), m));
}
}
@@ -613,7 +613,7 @@ namespace ARMeilleure.Instructions
}
else
{
EmitScalarUnaryOpF32(context, (op1) => EmitUnaryMathCall(context, nameof(Math.Truncate), op1));
EmitScalarUnaryOpF32(context, (op1) => EmitUnaryMathCall(context, nameof(MathHelper.Truncate), op1));
}
}

View File

@@ -460,8 +460,8 @@ namespace ARMeilleure.Instructions
IOpCodeSimd op = (IOpCodeSimd)context.CurrOp;
MethodInfo info = (op.Size & 1) == 0
? typeof(MathF).GetMethod(name, [typeof(float)])
: typeof(Math).GetMethod(name, [typeof(double)]);
? typeof(MathHelperF).GetMethod(name, [typeof(float)])
: typeof(MathHelper).GetMethod(name, [typeof(double)]);
return context.Call(info, n);
}
@@ -470,11 +470,11 @@ namespace ARMeilleure.Instructions
{
IOpCodeSimd op = (IOpCodeSimd)context.CurrOp;
string name = nameof(Math.Round);
string name = nameof(MathHelper.Round);
MethodInfo info = (op.Size & 1) == 0
? typeof(MathF).GetMethod(name, [typeof(float), typeof(MidpointRounding)])
: typeof(Math).GetMethod(name, [typeof(double), typeof(MidpointRounding)]);
? typeof(MathHelperF).GetMethod(name, [typeof(float), typeof(int)])
: typeof(MathHelper).GetMethod(name, [typeof(double), typeof(int)]);
return context.Call(info, n, Const((int)roundMode));
}
@@ -510,16 +510,16 @@ namespace ARMeilleure.Instructions
context.MarkLabel(lbl1);
context.BranchIf(lbl2, rMode, rP, Comparison.NotEqual);
context.Copy(res, EmitUnaryMathCall(context, nameof(Math.Ceiling), op));
context.Copy(res, EmitUnaryMathCall(context, nameof(MathHelper.Ceiling), op));
context.Branch(lblEnd);
context.MarkLabel(lbl2);
context.BranchIf(lbl3, rMode, rM, Comparison.NotEqual);
context.Copy(res, EmitUnaryMathCall(context, nameof(Math.Floor), op));
context.Copy(res, EmitUnaryMathCall(context, nameof(MathHelper.Floor), op));
context.Branch(lblEnd);
context.MarkLabel(lbl3);
context.Copy(res, EmitUnaryMathCall(context, nameof(Math.Truncate), op));
context.Copy(res, EmitUnaryMathCall(context, nameof(MathHelper.Truncate), op));
context.Branch(lblEnd);
context.MarkLabel(lblEnd);

View File

@@ -29,10 +29,10 @@ namespace ARMeilleure.Instructions
switch (GetPackedId(op))
{
case 0b11_011_0000_0000_001:
info = NativeInterface.Type.GetMethod(nameof(NativeInterface.GetCtrEl0));
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCtrEl0));
break;
case 0b11_011_0000_0000_111:
info = NativeInterface.Type.GetMethod(nameof(NativeInterface.GetDczidEl0));
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetDczidEl0));
break;
case 0b11_011_0100_0010_000:
EmitGetNzcv(context);
@@ -53,13 +53,13 @@ namespace ARMeilleure.Instructions
EmitGetTpidr2El0(context);
return;
case 0b11_011_1110_0000_000:
info = NativeInterface.Type.GetMethod(nameof(NativeInterface.GetCntfrqEl0));
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntfrqEl0));
break;
case 0b11_011_1110_0000_001:
info = NativeInterface.Type.GetMethod(nameof(NativeInterface.GetCntpctEl0));
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntpctEl0));
break;
case 0b11_011_1110_0000_010:
info = NativeInterface.Type.GetMethod(nameof(NativeInterface.GetCntvctEl0));
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntvctEl0));
break;
default:
@@ -131,7 +131,7 @@ namespace ARMeilleure.Instructions
case 0b11_011_0111_0101_001: // IC IVAU
Operand target = Register(op.Rt, RegisterType.Integer, OperandType.I64);
context.Call(NativeInterface.Type.GetMethod(nameof(NativeInterface.InvalidateCacheLine)), target);
context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.InvalidateCacheLine)), target);
break;
}
}

View File

@@ -127,7 +127,7 @@ namespace ARMeilleure.Instructions
// Timer.
14 => opc switch
{
0 => NativeInterface.Type.GetMethod(nameof(NativeInterface.GetCntpctEl0)),
0 => typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntpctEl0)),
_ => throw new NotImplementedException($"Unknown MRRC Opc1 0x{opc:X} at 0x{op.Address:X} (0x{op.RawOpCode:X})."),
},
_ => throw new NotImplementedException($"Unknown MRRC 0x{op.RawOpCode:X} at 0x{op.Address:X}."),

View File

@@ -0,0 +1,71 @@
using System;
using System.Runtime.InteropServices;
namespace ARMeilleure.Instructions
{
static class MathHelper
{
[UnmanagedCallersOnly]
public static double Abs(double value)
{
return Math.Abs(value);
}
[UnmanagedCallersOnly]
public static double Ceiling(double value)
{
return Math.Ceiling(value);
}
[UnmanagedCallersOnly]
public static double Floor(double value)
{
return Math.Floor(value);
}
[UnmanagedCallersOnly]
public static double Round(double value, int mode)
{
return Math.Round(value, (MidpointRounding)mode);
}
[UnmanagedCallersOnly]
public static double Truncate(double value)
{
return Math.Truncate(value);
}
}
static class MathHelperF
{
[UnmanagedCallersOnly]
public static float Abs(float value)
{
return MathF.Abs(value);
}
[UnmanagedCallersOnly]
public static float Ceiling(float value)
{
return MathF.Ceiling(value);
}
[UnmanagedCallersOnly]
public static float Floor(float value)
{
return MathF.Floor(value);
}
[UnmanagedCallersOnly]
public static float Round(float value, int mode)
{
return MathF.Round(value, (MidpointRounding)mode);
}
[UnmanagedCallersOnly]
public static float Truncate(float value)
{
return MathF.Truncate(value);
}
}
}

View File

@@ -2,15 +2,12 @@ using ARMeilleure.Memory;
using ARMeilleure.State;
using ARMeilleure.Translation;
using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
namespace ARMeilleure.Instructions
{
static class NativeInterface
{
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]
public static readonly Type Type = typeof(NativeInterface);
private class ThreadContext
{
public ExecutionContext Context { get; }
@@ -38,6 +35,7 @@ namespace ARMeilleure.Instructions
Context = null;
}
[UnmanagedCallersOnly]
public static void Break(ulong address, int imm)
{
Statistics.PauseTimer();
@@ -47,6 +45,7 @@ namespace ARMeilleure.Instructions
Statistics.ResumeTimer();
}
[UnmanagedCallersOnly]
public static void SupervisorCall(ulong address, int imm)
{
Statistics.PauseTimer();
@@ -56,6 +55,7 @@ namespace ARMeilleure.Instructions
Statistics.ResumeTimer();
}
[UnmanagedCallersOnly]
public static void Undefined(ulong address, int opCode)
{
Statistics.PauseTimer();
@@ -66,26 +66,31 @@ namespace ARMeilleure.Instructions
}
#region "System registers"
[UnmanagedCallersOnly]
public static ulong GetCtrEl0()
{
return GetContext().CtrEl0;
}
[UnmanagedCallersOnly]
public static ulong GetDczidEl0()
{
return GetContext().DczidEl0;
}
[UnmanagedCallersOnly]
public static ulong GetCntfrqEl0()
{
return GetContext().CntfrqEl0;
}
[UnmanagedCallersOnly]
public static ulong GetCntpctEl0()
{
return GetContext().CntpctEl0;
}
[UnmanagedCallersOnly]
public static ulong GetCntvctEl0()
{
return GetContext().CntvctEl0;
@@ -93,26 +98,31 @@ namespace ARMeilleure.Instructions
#endregion
#region "Read"
[UnmanagedCallersOnly]
public static byte ReadByte(ulong address)
{
return GetMemoryManager().ReadGuest<byte>(address);
}
[UnmanagedCallersOnly]
public static ushort ReadUInt16(ulong address)
{
return GetMemoryManager().ReadGuest<ushort>(address);
}
[UnmanagedCallersOnly]
public static uint ReadUInt32(ulong address)
{
return GetMemoryManager().ReadGuest<uint>(address);
}
[UnmanagedCallersOnly]
public static ulong ReadUInt64(ulong address)
{
return GetMemoryManager().ReadGuest<ulong>(address);
}
[UnmanagedCallersOnly]
public static V128 ReadVector128(ulong address)
{
return GetMemoryManager().ReadGuest<V128>(address);
@@ -120,47 +130,56 @@ namespace ARMeilleure.Instructions
#endregion
#region "Write"
[UnmanagedCallersOnly]
public static void WriteByte(ulong address, byte value)
{
GetMemoryManager().WriteGuest(address, value);
}
[UnmanagedCallersOnly]
public static void WriteUInt16(ulong address, ushort value)
{
GetMemoryManager().WriteGuest(address, value);
}
[UnmanagedCallersOnly]
public static void WriteUInt32(ulong address, uint value)
{
GetMemoryManager().WriteGuest(address, value);
}
[UnmanagedCallersOnly]
public static void WriteUInt64(ulong address, ulong value)
{
GetMemoryManager().WriteGuest(address, value);
}
[UnmanagedCallersOnly]
public static void WriteVector128(ulong address, V128 value)
{
GetMemoryManager().WriteGuest(address, value);
}
#endregion
[UnmanagedCallersOnly]
public static void EnqueueForRejit(ulong address)
{
Context.Translator.EnqueueForRejit(address, GetContext().ExecutionMode);
}
public static void SignalMemoryTracking(ulong address, ulong size, bool write)
[UnmanagedCallersOnly]
public static void SignalMemoryTracking(ulong address, ulong size, byte write)
{
GetMemoryManager().SignalMemoryTracking(address, size, write);
GetMemoryManager().SignalMemoryTracking(address, size, write == 1);
}
[UnmanagedCallersOnly]
public static void ThrowInvalidMemoryAccess(ulong address)
{
throw new InvalidAccessException(address);
}
[UnmanagedCallersOnly]
public static ulong GetFunctionAddress(ulong address)
{
TranslatedFunction function = Context.Translator.GetOrTranslate(address, GetContext().ExecutionMode);
@@ -168,12 +187,14 @@ namespace ARMeilleure.Instructions
return (ulong)function.FuncPointer.ToInt64();
}
[UnmanagedCallersOnly]
public static void InvalidateCacheLine(ulong address)
{
Context.Translator.InvalidateJitCacheRegion(address, InstEmit.DczSizeInBytes);
}
public static bool CheckSynchronization()
[UnmanagedCallersOnly]
public static byte CheckSynchronization()
{
Statistics.PauseTimer();
@@ -183,7 +204,7 @@ namespace ARMeilleure.Instructions
Statistics.ResumeTimer();
return context.Running;
return (byte)(context.Running ? 1 : 0);
}
public static ExecutionContext GetContext()

View File

@@ -1,11 +1,13 @@
using ARMeilleure.State;
using System;
using System.Runtime.InteropServices;
namespace ARMeilleure.Instructions
{
static class SoftFallback
{
#region "ShrImm64"
[UnmanagedCallersOnly]
public static long SignedShrImm64(long value, long roundConst, int shift)
{
if (roundConst == 0L)
@@ -48,6 +50,7 @@ namespace ARMeilleure.Instructions
}
}
[UnmanagedCallersOnly]
public static ulong UnsignedShrImm64(ulong value, long roundConst, int shift)
{
if (roundConst == 0L)
@@ -92,6 +95,7 @@ namespace ARMeilleure.Instructions
#endregion
#region "Saturation"
[UnmanagedCallersOnly]
public static int SatF32ToS32(float value)
{
if (float.IsNaN(value))
@@ -103,6 +107,7 @@ namespace ARMeilleure.Instructions
value <= int.MinValue ? int.MinValue : (int)value;
}
[UnmanagedCallersOnly]
public static long SatF32ToS64(float value)
{
if (float.IsNaN(value))
@@ -114,6 +119,7 @@ namespace ARMeilleure.Instructions
value <= long.MinValue ? long.MinValue : (long)value;
}
[UnmanagedCallersOnly]
public static uint SatF32ToU32(float value)
{
if (float.IsNaN(value))
@@ -125,6 +131,7 @@ namespace ARMeilleure.Instructions
value <= uint.MinValue ? uint.MinValue : (uint)value;
}
[UnmanagedCallersOnly]
public static ulong SatF32ToU64(float value)
{
if (float.IsNaN(value))
@@ -136,6 +143,7 @@ namespace ARMeilleure.Instructions
value <= ulong.MinValue ? ulong.MinValue : (ulong)value;
}
[UnmanagedCallersOnly]
public static int SatF64ToS32(double value)
{
if (double.IsNaN(value))
@@ -147,6 +155,7 @@ namespace ARMeilleure.Instructions
value <= int.MinValue ? int.MinValue : (int)value;
}
[UnmanagedCallersOnly]
public static long SatF64ToS64(double value)
{
if (double.IsNaN(value))
@@ -158,6 +167,7 @@ namespace ARMeilleure.Instructions
value <= long.MinValue ? long.MinValue : (long)value;
}
[UnmanagedCallersOnly]
public static uint SatF64ToU32(double value)
{
if (double.IsNaN(value))
@@ -169,6 +179,7 @@ namespace ARMeilleure.Instructions
value <= uint.MinValue ? uint.MinValue : (uint)value;
}
[UnmanagedCallersOnly]
public static ulong SatF64ToU64(double value)
{
if (double.IsNaN(value))
@@ -182,6 +193,7 @@ namespace ARMeilleure.Instructions
#endregion
#region "Count"
[UnmanagedCallersOnly]
public static ulong CountLeadingSigns(ulong value, int size) // size is 8, 16, 32 or 64 (SIMD&FP or Base Inst.).
{
value ^= value >> 1;
@@ -201,6 +213,7 @@ namespace ARMeilleure.Instructions
private static ReadOnlySpan<byte> ClzNibbleTbl => [4, 3, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0];
[UnmanagedCallersOnly]
public static ulong CountLeadingZeros(ulong value, int size) // size is 8, 16, 32 or 64 (SIMD&FP or Base Inst.).
{
if (value == 0ul)
@@ -224,41 +237,49 @@ namespace ARMeilleure.Instructions
#endregion
#region "Table"
[UnmanagedCallersOnly]
public static V128 Tbl1(V128 vector, int bytes, V128 tb0)
{
return TblOrTbx(default, vector, bytes, tb0);
}
[UnmanagedCallersOnly]
public static V128 Tbl2(V128 vector, int bytes, V128 tb0, V128 tb1)
{
return TblOrTbx(default, vector, bytes, tb0, tb1);
}
[UnmanagedCallersOnly]
public static V128 Tbl3(V128 vector, int bytes, V128 tb0, V128 tb1, V128 tb2)
{
return TblOrTbx(default, vector, bytes, tb0, tb1, tb2);
}
[UnmanagedCallersOnly]
public static V128 Tbl4(V128 vector, int bytes, V128 tb0, V128 tb1, V128 tb2, V128 tb3)
{
return TblOrTbx(default, vector, bytes, tb0, tb1, tb2, tb3);
}
[UnmanagedCallersOnly]
public static V128 Tbx1(V128 dest, V128 vector, int bytes, V128 tb0)
{
return TblOrTbx(dest, vector, bytes, tb0);
}
[UnmanagedCallersOnly]
public static V128 Tbx2(V128 dest, V128 vector, int bytes, V128 tb0, V128 tb1)
{
return TblOrTbx(dest, vector, bytes, tb0, tb1);
}
[UnmanagedCallersOnly]
public static V128 Tbx3(V128 dest, V128 vector, int bytes, V128 tb0, V128 tb1, V128 tb2)
{
return TblOrTbx(dest, vector, bytes, tb0, tb1, tb2);
}
[UnmanagedCallersOnly]
public static V128 Tbx4(V128 dest, V128 vector, int bytes, V128 tb0, V128 tb1, V128 tb2, V128 tb3)
{
return TblOrTbx(dest, vector, bytes, tb0, tb1, tb2, tb3);
@@ -300,14 +321,22 @@ namespace ARMeilleure.Instructions
private const uint Crc32RevPoly = 0xedb88320;
private const uint Crc32cRevPoly = 0x82f63b78;
[UnmanagedCallersOnly]
public static uint Crc32b(uint crc, byte value) => Crc32(crc, Crc32RevPoly, value);
[UnmanagedCallersOnly]
public static uint Crc32h(uint crc, ushort value) => Crc32h(crc, Crc32RevPoly, value);
[UnmanagedCallersOnly]
public static uint Crc32w(uint crc, uint value) => Crc32w(crc, Crc32RevPoly, value);
[UnmanagedCallersOnly]
public static uint Crc32x(uint crc, ulong value) => Crc32x(crc, Crc32RevPoly, value);
[UnmanagedCallersOnly]
public static uint Crc32cb(uint crc, byte value) => Crc32(crc, Crc32cRevPoly, value);
[UnmanagedCallersOnly]
public static uint Crc32ch(uint crc, ushort value) => Crc32h(crc, Crc32cRevPoly, value);
[UnmanagedCallersOnly]
public static uint Crc32cw(uint crc, uint value) => Crc32w(crc, Crc32cRevPoly, value);
[UnmanagedCallersOnly]
public static uint Crc32cx(uint crc, ulong value) => Crc32x(crc, Crc32cRevPoly, value);
private static uint Crc32h(uint crc, uint poly, ushort val)
@@ -358,21 +387,25 @@ namespace ARMeilleure.Instructions
#endregion
#region "Aes"
[UnmanagedCallersOnly]
public static V128 Decrypt(V128 value, V128 roundKey)
{
return CryptoHelper.AesInvSubBytes(CryptoHelper.AesInvShiftRows(value ^ roundKey));
}
[UnmanagedCallersOnly]
public static V128 Encrypt(V128 value, V128 roundKey)
{
return CryptoHelper.AesSubBytes(CryptoHelper.AesShiftRows(value ^ roundKey));
}
[UnmanagedCallersOnly]
public static V128 InverseMixColumns(V128 value)
{
return CryptoHelper.AesInvMixColumns(value);
}
[UnmanagedCallersOnly]
public static V128 MixColumns(V128 value)
{
return CryptoHelper.AesMixColumns(value);
@@ -380,6 +413,7 @@ namespace ARMeilleure.Instructions
#endregion
#region "Sha1"
[UnmanagedCallersOnly]
public static V128 HashChoose(V128 hash_abcd, uint hash_e, V128 wk)
{
for (int e = 0; e <= 3; e++)
@@ -400,11 +434,13 @@ namespace ARMeilleure.Instructions
return hash_abcd;
}
[UnmanagedCallersOnly]
public static uint FixedRotate(uint hash_e)
{
return hash_e.Rol(30);
}
[UnmanagedCallersOnly]
public static V128 HashMajority(V128 hash_abcd, uint hash_e, V128 wk)
{
for (int e = 0; e <= 3; e++)
@@ -425,6 +461,7 @@ namespace ARMeilleure.Instructions
return hash_abcd;
}
[UnmanagedCallersOnly]
public static V128 HashParity(V128 hash_abcd, uint hash_e, V128 wk)
{
for (int e = 0; e <= 3; e++)
@@ -445,6 +482,7 @@ namespace ARMeilleure.Instructions
return hash_abcd;
}
[UnmanagedCallersOnly]
public static V128 Sha1SchedulePart1(V128 w0_3, V128 w4_7, V128 w8_11)
{
ulong t2 = w4_7.Extract<ulong>(0);
@@ -455,6 +493,7 @@ namespace ARMeilleure.Instructions
return result ^ (w0_3 ^ w8_11);
}
[UnmanagedCallersOnly]
public static V128 Sha1SchedulePart2(V128 tw0_3, V128 w12_15)
{
V128 t = tw0_3 ^ (w12_15 >> 32);
@@ -499,16 +538,19 @@ namespace ARMeilleure.Instructions
#endregion
#region "Sha256"
[UnmanagedCallersOnly]
public static V128 HashLower(V128 hash_abcd, V128 hash_efgh, V128 wk)
{
return Sha256Hash(hash_abcd, hash_efgh, wk, part1: true);
}
[UnmanagedCallersOnly]
public static V128 HashUpper(V128 hash_abcd, V128 hash_efgh, V128 wk)
{
return Sha256Hash(hash_abcd, hash_efgh, wk, part1: false);
}
[UnmanagedCallersOnly]
public static V128 Sha256SchedulePart1(V128 w0_3, V128 w4_7)
{
V128 result = new();
@@ -527,6 +569,7 @@ namespace ARMeilleure.Instructions
return result;
}
[UnmanagedCallersOnly]
public static V128 Sha256SchedulePart2(V128 w0_3, V128 w8_11, V128 w12_15)
{
V128 result = new();
@@ -628,6 +671,7 @@ namespace ARMeilleure.Instructions
}
#endregion
[UnmanagedCallersOnly]
public static V128 PolynomialMult64_128(ulong op1, ulong op2)
{
V128 result = V128.Zero;

View File

@@ -1,6 +1,7 @@
using ARMeilleure.State;
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace ARMeilleure.Instructions
{
@@ -312,6 +313,7 @@ namespace ARMeilleure.Instructions
static class SoftFloat16_32
{
[UnmanagedCallersOnly]
public static float FPConvert(ushort valueBits)
{
ExecutionContext context = NativeInterface.GetContext();
@@ -487,6 +489,7 @@ namespace ARMeilleure.Instructions
static class SoftFloat16_64
{
[UnmanagedCallersOnly]
public static double FPConvert(ushort valueBits)
{
ExecutionContext context = NativeInterface.GetContext();
@@ -662,6 +665,7 @@ namespace ARMeilleure.Instructions
static class SoftFloat32_16
{
[UnmanagedCallersOnly]
public static ushort FPConvert(float value)
{
ExecutionContext context = NativeInterface.GetContext();
@@ -781,12 +785,19 @@ namespace ARMeilleure.Instructions
static class SoftFloat32
{
[UnmanagedCallersOnly]
public static float FPAdd(float value1, float value2)
{
return FPAddFpscr(value1, value2, false);
return FPAddFpscrImpl(value1, value2, false);
}
public static float FPAddFpscr(float value1, float value2, bool standardFpscr)
[UnmanagedCallersOnly]
public static float FPAddFpscr(float value1, float value2, byte standardFpscr)
{
return FPAddFpscrImpl(value1, value2, standardFpscr == 1);
}
private static float FPAddFpscrImpl(float value1, float value2, bool standardFpscr)
{
ExecutionContext context = NativeInterface.GetContext();
FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
@@ -837,7 +848,8 @@ namespace ARMeilleure.Instructions
return result;
}
public static int FPCompare(float value1, float value2, bool signalNaNs)
[UnmanagedCallersOnly]
public static int FPCompare(float value1, float value2, byte signalNaNs)
{
ExecutionContext context = NativeInterface.GetContext();
FPCR fpcr = context.Fpcr;
@@ -851,7 +863,7 @@ namespace ARMeilleure.Instructions
{
result = 0b0011;
if (type1 == FPType.SNaN || type2 == FPType.SNaN || signalNaNs)
if (type1 == FPType.SNaN || type2 == FPType.SNaN || signalNaNs == 1)
{
SoftFloat.FPProcessException(FPException.InvalidOp, context, fpcr);
}
@@ -875,12 +887,13 @@ namespace ARMeilleure.Instructions
return result;
}
[UnmanagedCallersOnly]
public static float FPCompareEQ(float value1, float value2)
{
return FPCompareEQFpscr(value1, value2, false);
return FPCompareEQFpscrImpl(value1, value2, false);
}
public static float FPCompareEQFpscr(float value1, float value2, bool standardFpscr)
private static float FPCompareEQFpscrImpl(float value1, float value2, bool standardFpscr)
{
ExecutionContext context = NativeInterface.GetContext();
FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
@@ -907,12 +920,25 @@ namespace ARMeilleure.Instructions
return result;
}
public static float FPCompareGE(float value1, float value2)
[UnmanagedCallersOnly]
public static float FPCompareEQFpscr(float value1, float value2, byte standardFpscr)
{
return FPCompareGEFpscr(value1, value2, false);
return FPCompareEQFpscrImpl(value1, value2, standardFpscr == 1);
}
public static float FPCompareGEFpscr(float value1, float value2, bool standardFpscr)
[UnmanagedCallersOnly]
public static float FPCompareGE(float value1, float value2)
{
return FPCompareGEFpscrImpl(value1, value2, false);
}
[UnmanagedCallersOnly]
public static float FPCompareGEFpscr(float value1, float value2, byte standardFpscr)
{
return FPCompareGEFpscrImpl(value1, value2, standardFpscr == 1);
}
private static float FPCompareGEFpscrImpl(float value1, float value2, bool standardFpscr)
{
ExecutionContext context = NativeInterface.GetContext();
FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
@@ -936,12 +962,19 @@ namespace ARMeilleure.Instructions
return result;
}
[UnmanagedCallersOnly]
public static float FPCompareGT(float value1, float value2)
{
return FPCompareGTFpscr(value1, value2, false);
return FPCompareGTFpscrImpl(value1, value2, false);
}
public static float FPCompareGTFpscr(float value1, float value2, bool standardFpscr)
[UnmanagedCallersOnly]
public static float FPCompareGTFpscr(float value1, float value2, byte standardFpscr)
{
return FPCompareGTFpscrImpl(value1, value2, standardFpscr == 1);
}
private static float FPCompareGTFpscrImpl(float value1, float value2, bool standardFpscr)
{
ExecutionContext context = NativeInterface.GetContext();
FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
@@ -965,26 +998,31 @@ namespace ARMeilleure.Instructions
return result;
}
[UnmanagedCallersOnly]
public static float FPCompareLE(float value1, float value2)
{
return FPCompareGE(value2, value1);
return FPCompareGEFpscrImpl(value2, value1, false);
}
[UnmanagedCallersOnly]
public static float FPCompareLT(float value1, float value2)
{
return FPCompareGT(value2, value1);
return FPCompareGTFpscrImpl(value2, value1, false);
}
public static float FPCompareLEFpscr(float value1, float value2, bool standardFpscr)
[UnmanagedCallersOnly]
public static float FPCompareLEFpscr(float value1, float value2, byte standardFpscr)
{
return FPCompareGEFpscr(value2, value1, standardFpscr);
return FPCompareGEFpscrImpl(value2, value1, standardFpscr == 1);
}
public static float FPCompareLTFpscr(float value1, float value2, bool standardFpscr)
[UnmanagedCallersOnly]
public static float FPCompareLTFpscr(float value1, float value2, byte standardFpscr)
{
return FPCompareGTFpscr(value2, value1, standardFpscr);
return FPCompareGEFpscrImpl(value2, value1, standardFpscr == 1);
}
[UnmanagedCallersOnly]
public static float FPDiv(float value1, float value2)
{
ExecutionContext context = NativeInterface.GetContext();
@@ -1037,12 +1075,19 @@ namespace ARMeilleure.Instructions
return result;
}
[UnmanagedCallersOnly]
public static float FPMax(float value1, float value2)
{
return FPMaxFpscr(value1, value2, false);
return FPMaxFpscrImpl(value1, value2, false);
}
public static float FPMaxFpscr(float value1, float value2, bool standardFpscr)
[UnmanagedCallersOnly]
public static float FPMaxFpscr(float value1, float value2, byte standardFpscr)
{
return FPMaxFpscrImpl(value1, value2, standardFpscr == 1);
}
private static float FPMaxFpscrImpl(float value1, float value2, bool standardFpscr)
{
ExecutionContext context = NativeInterface.GetContext();
FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
@@ -1103,12 +1148,13 @@ namespace ARMeilleure.Instructions
return result;
}
[UnmanagedCallersOnly]
public static float FPMaxNum(float value1, float value2)
{
return FPMaxNumFpscr(value1, value2, false);
return FPMaxNumFpscrImpl(value1, value2, false);
}
public static float FPMaxNumFpscr(float value1, float value2, bool standardFpscr)
private static float FPMaxNumFpscrImpl(float value1, float value2, bool standardFpscr)
{
ExecutionContext context = NativeInterface.GetContext();
FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
@@ -1125,15 +1171,28 @@ namespace ARMeilleure.Instructions
value2 = FPInfinity(true);
}
return FPMaxFpscr(value1, value2, standardFpscr);
return FPMaxFpscrImpl(value1, value2, standardFpscr);
}
[UnmanagedCallersOnly]
public static float FPMaxNumFpscr(float value1, float value2, byte standardFpscr)
{
return FPMaxNumFpscrImpl(value1, value2, standardFpscr == 1);
}
[UnmanagedCallersOnly]
public static float FPMin(float value1, float value2)
{
return FPMinFpscr(value1, value2, false);
return FPMinFpscrImpl(value1, value2, false);
}
public static float FPMinFpscr(float value1, float value2, bool standardFpscr)
[UnmanagedCallersOnly]
public static float FPMinFpscr(float value1, float value2, byte standardFpscr)
{
return FPMinFpscrImpl(value1, value2, standardFpscr == 1);
}
private static float FPMinFpscrImpl(float value1, float value2, bool standardFpscr)
{
ExecutionContext context = NativeInterface.GetContext();
FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
@@ -1194,12 +1253,19 @@ namespace ARMeilleure.Instructions
return result;
}
[UnmanagedCallersOnly]
public static float FPMinNum(float value1, float value2)
{
return FPMinNumFpscr(value1, value2, false);
return FPMinNumFpscrImpl(value1, value2, false);
}
public static float FPMinNumFpscr(float value1, float value2, bool standardFpscr)
[UnmanagedCallersOnly]
public static float FPMinNumFpscr(float value1, float value2, byte standardFpscr)
{
return FPMinNumFpscrImpl(value1, value2, standardFpscr == 1);
}
private static float FPMinNumFpscrImpl(float value1, float value2, bool standardFpscr)
{
ExecutionContext context = NativeInterface.GetContext();
FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
@@ -1216,15 +1282,22 @@ namespace ARMeilleure.Instructions
value2 = FPInfinity(false);
}
return FPMinFpscr(value1, value2, standardFpscr);
return FPMinFpscrImpl(value1, value2, standardFpscr);
}
[UnmanagedCallersOnly]
public static float FPMul(float value1, float value2)
{
return FPMulFpscr(value1, value2, false);
return FPMulFpscrImpl(value1, value2, false);
}
public static float FPMulFpscr(float value1, float value2, bool standardFpscr)
[UnmanagedCallersOnly]
public static float FPMulFpscr(float value1, float value2, byte standardFpscr)
{
return FPMulFpscrImpl(value1, value2, standardFpscr == 1);
}
private static float FPMulFpscrImpl(float value1, float value2, bool standardFpscr)
{
ExecutionContext context = NativeInterface.GetContext();
FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
@@ -1271,12 +1344,19 @@ namespace ARMeilleure.Instructions
return result;
}
[UnmanagedCallersOnly]
public static float FPMulAdd(float valueA, float value1, float value2)
{
return FPMulAddFpscr(valueA, value1, value2, false);
return FPMulAddFpscrImpl(valueA, value1, value2, false);
}
public static float FPMulAddFpscr(float valueA, float value1, float value2, bool standardFpscr)
[UnmanagedCallersOnly]
public static float FPMulAddFpscr(float valueA, float value1, float value2, byte standardFpscr)
{
return FPMulAddFpscrImpl(valueA, value1, value2, standardFpscr == 1);
}
private static float FPMulAddFpscrImpl(float valueA, float value1, float value2, bool standardFpscr)
{
ExecutionContext context = NativeInterface.GetContext();
FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
@@ -1342,20 +1422,23 @@ namespace ARMeilleure.Instructions
return result;
}
[UnmanagedCallersOnly]
public static float FPMulSub(float valueA, float value1, float value2)
{
value1 = value1.FPNeg();
return FPMulAdd(valueA, value1, value2);
return FPMulAddFpscrImpl(valueA, value1, value2, false);
}
public static float FPMulSubFpscr(float valueA, float value1, float value2, bool standardFpscr)
[UnmanagedCallersOnly]
public static float FPMulSubFpscr(float valueA, float value1, float value2, byte standardFpscr)
{
value1 = value1.FPNeg();
return FPMulAddFpscr(valueA, value1, value2, standardFpscr);
return FPMulAddFpscrImpl(valueA, value1, value2, standardFpscr == 1);
}
[UnmanagedCallersOnly]
public static float FPMulX(float value1, float value2)
{
ExecutionContext context = NativeInterface.GetContext();
@@ -1401,27 +1484,36 @@ namespace ARMeilleure.Instructions
return result;
}
[UnmanagedCallersOnly]
public static float FPNegMulAdd(float valueA, float value1, float value2)
{
valueA = valueA.FPNeg();
value1 = value1.FPNeg();
return FPMulAdd(valueA, value1, value2);
return FPMulAddFpscrImpl(valueA, value1, value2, false);
}
[UnmanagedCallersOnly]
public static float FPNegMulSub(float valueA, float value1, float value2)
{
valueA = valueA.FPNeg();
return FPMulAdd(valueA, value1, value2);
return FPMulAddFpscrImpl(valueA, value1, value2, false);
}
[UnmanagedCallersOnly]
public static float FPRecipEstimate(float value)
{
return FPRecipEstimateFpscr(value, false);
return FPRecipEstimateFpscrImpl(value, false);
}
public static float FPRecipEstimateFpscr(float value, bool standardFpscr)
[UnmanagedCallersOnly]
public static float FPRecipEstimateFpscr(float value, byte standardFpscr)
{
return FPRecipEstimateFpscrImpl(value, standardFpscr == 1);
}
private static float FPRecipEstimateFpscrImpl(float value, bool standardFpscr)
{
ExecutionContext context = NativeInterface.GetContext();
FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
@@ -1508,6 +1600,7 @@ namespace ARMeilleure.Instructions
return result;
}
[UnmanagedCallersOnly]
public static float FPRecipStep(float value1, float value2)
{
ExecutionContext context = NativeInterface.GetContext();
@@ -1533,15 +1626,16 @@ namespace ARMeilleure.Instructions
}
else
{
product = FPMulFpscr(value1, value2, true);
product = FPMulFpscrImpl(value1, value2, true);
}
result = FPSubFpscr(FPTwo(false), product, true);
result = FPSubFpscrImpl(FPTwo(false), product, true);
}
return result;
}
[UnmanagedCallersOnly]
public static float FPRecipStepFused(float value1, float value2)
{
ExecutionContext context = NativeInterface.GetContext();
@@ -1585,6 +1679,7 @@ namespace ARMeilleure.Instructions
return result;
}
[UnmanagedCallersOnly]
public static float FPRecpX(float value)
{
ExecutionContext context = NativeInterface.GetContext();
@@ -1610,12 +1705,19 @@ namespace ARMeilleure.Instructions
return result;
}
[UnmanagedCallersOnly]
public static float FPRSqrtEstimate(float value)
{
return FPRSqrtEstimateFpscr(value, false);
return FPRSqrtEstimateFpscrImpl(value, false);
}
public static float FPRSqrtEstimateFpscr(float value, bool standardFpscr)
[UnmanagedCallersOnly]
public static float FPRSqrtEstimateFpscr(float value, byte standardFpscr)
{
return FPRSqrtEstimateFpscrImpl(value, standardFpscr == 1);
}
private static float FPRSqrtEstimateFpscrImpl(float value, bool standardFpscr)
{
ExecutionContext context = NativeInterface.GetContext();
FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
@@ -1729,6 +1831,7 @@ namespace ARMeilleure.Instructions
return result;
}
[UnmanagedCallersOnly]
public static float FPRSqrtStep(float value1, float value2)
{
ExecutionContext context = NativeInterface.GetContext();
@@ -1754,7 +1857,7 @@ namespace ARMeilleure.Instructions
}
else
{
product = FPMulFpscr(value1, value2, true);
product = FPMulFpscrImpl(value1, value2, true);
}
result = FPHalvedSub(FPThree(false), product, context, fpcr);
@@ -1763,6 +1866,7 @@ namespace ARMeilleure.Instructions
return result;
}
[UnmanagedCallersOnly]
public static float FPRSqrtStepFused(float value1, float value2)
{
ExecutionContext context = NativeInterface.GetContext();
@@ -1806,6 +1910,7 @@ namespace ARMeilleure.Instructions
return result;
}
[UnmanagedCallersOnly]
public static float FPSqrt(float value)
{
ExecutionContext context = NativeInterface.GetContext();
@@ -1848,12 +1953,13 @@ namespace ARMeilleure.Instructions
return result;
}
[UnmanagedCallersOnly]
public static float FPSub(float value1, float value2)
{
return FPSubFpscr(value1, value2, false);
return FPSubFpscrImpl(value1, value2, false);
}
public static float FPSubFpscr(float value1, float value2, bool standardFpscr)
private static float FPSubFpscrImpl(float value1, float value2, bool standardFpscr)
{
ExecutionContext context = NativeInterface.GetContext();
FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
@@ -2094,6 +2200,7 @@ namespace ARMeilleure.Instructions
static class SoftFloat64_16
{
[UnmanagedCallersOnly]
public static ushort FPConvert(double value)
{
ExecutionContext context = NativeInterface.GetContext();
@@ -2213,12 +2320,19 @@ namespace ARMeilleure.Instructions
static class SoftFloat64
{
[UnmanagedCallersOnly]
public static double FPAdd(double value1, double value2)
{
return FPAddFpscr(value1, value2, false);
return FPAddFpscrImpl(value1, value2, false);
}
public static double FPAddFpscr(double value1, double value2, bool standardFpscr)
[UnmanagedCallersOnly]
public static double FPAddFpscr(double value1, double value2, byte standardFpscr)
{
return FPAddFpscrImpl(value1, value2, standardFpscr == 1);
}
private static double FPAddFpscrImpl(double value1, double value2, bool standardFpscr)
{
ExecutionContext context = NativeInterface.GetContext();
FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
@@ -2269,7 +2383,8 @@ namespace ARMeilleure.Instructions
return result;
}
public static int FPCompare(double value1, double value2, bool signalNaNs)
[UnmanagedCallersOnly]
public static int FPCompare(double value1, double value2, byte signalNaNs)
{
ExecutionContext context = NativeInterface.GetContext();
FPCR fpcr = context.Fpcr;
@@ -2283,7 +2398,7 @@ namespace ARMeilleure.Instructions
{
result = 0b0011;
if (type1 == FPType.SNaN || type2 == FPType.SNaN || signalNaNs)
if (type1 == FPType.SNaN || type2 == FPType.SNaN || signalNaNs == 1)
{
SoftFloat.FPProcessException(FPException.InvalidOp, context, fpcr);
}
@@ -2307,12 +2422,19 @@ namespace ARMeilleure.Instructions
return result;
}
[UnmanagedCallersOnly]
public static double FPCompareEQ(double value1, double value2)
{
return FPCompareEQFpscr(value1, value2, false);
return FPCompareEQFpscrImpl(value1, value2, false);
}
public static double FPCompareEQFpscr(double value1, double value2, bool standardFpscr)
[UnmanagedCallersOnly]
public static double FPCompareEQFpscr(double value1, double value2, byte standardFpscr)
{
return FPCompareEQFpscrImpl(value1, value2, standardFpscr == 1);
}
private static double FPCompareEQFpscrImpl(double value1, double value2, bool standardFpscr)
{
ExecutionContext context = NativeInterface.GetContext();
FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
@@ -2339,12 +2461,19 @@ namespace ARMeilleure.Instructions
return result;
}
[UnmanagedCallersOnly]
public static double FPCompareGE(double value1, double value2)
{
return FPCompareGEFpscr(value1, value2, false);
return FPCompareGEFpscrImpl(value1, value2, false);
}
public static double FPCompareGEFpscr(double value1, double value2, bool standardFpscr)
[UnmanagedCallersOnly]
public static double FPCompareGEFpscr(double value1, double value2, byte standardFpscr)
{
return FPCompareGEFpscrImpl(value1, value2, standardFpscr == 1);
}
private static double FPCompareGEFpscrImpl(double value1, double value2, bool standardFpscr)
{
ExecutionContext context = NativeInterface.GetContext();
FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
@@ -2368,12 +2497,19 @@ namespace ARMeilleure.Instructions
return result;
}
[UnmanagedCallersOnly]
public static double FPCompareGT(double value1, double value2)
{
return FPCompareGTFpscr(value1, value2, false);
return FPCompareGTFpscrImpl(value1, value2, false);
}
public static double FPCompareGTFpscr(double value1, double value2, bool standardFpscr)
[UnmanagedCallersOnly]
public static double FPCompareGTFpscr(double value1, double value2, byte standardFpscr)
{
return FPCompareGTFpscrImpl(value1, value2, standardFpscr == 1);
}
private static double FPCompareGTFpscrImpl(double value1, double value2, bool standardFpscr)
{
ExecutionContext context = NativeInterface.GetContext();
FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
@@ -2397,26 +2533,31 @@ namespace ARMeilleure.Instructions
return result;
}
[UnmanagedCallersOnly]
public static double FPCompareLE(double value1, double value2)
{
return FPCompareGE(value2, value1);
return FPCompareGEFpscrImpl(value2, value1, false);
}
[UnmanagedCallersOnly]
public static double FPCompareLT(double value1, double value2)
{
return FPCompareGT(value2, value1);
return FPCompareGTFpscrImpl(value2, value1, false);
}
public static double FPCompareLEFpscr(double value1, double value2, bool standardFpscr)
[UnmanagedCallersOnly]
public static double FPCompareLEFpscr(double value1, double value2, byte standardFpscr)
{
return FPCompareGEFpscr(value2, value1, standardFpscr);
return FPCompareGEFpscrImpl(value2, value1, standardFpscr == 1);
}
public static double FPCompareLTFpscr(double value1, double value2, bool standardFpscr)
[UnmanagedCallersOnly]
public static double FPCompareLTFpscr(double value1, double value2, byte standardFpscr)
{
return FPCompareGTFpscr(value2, value1, standardFpscr);
return FPCompareGTFpscrImpl(value2, value1, standardFpscr == 1);
}
[UnmanagedCallersOnly]
public static double FPDiv(double value1, double value2)
{
ExecutionContext context = NativeInterface.GetContext();
@@ -2469,12 +2610,19 @@ namespace ARMeilleure.Instructions
return result;
}
[UnmanagedCallersOnly]
public static double FPMax(double value1, double value2)
{
return FPMaxFpscr(value1, value2, false);
return FPMaxFpscrImpl(value1, value2, false);
}
public static double FPMaxFpscr(double value1, double value2, bool standardFpscr)
[UnmanagedCallersOnly]
public static double FPMaxFpscr(double value1, double value2, byte standardFpscr)
{
return FPMaxFpscrImpl(value1, value2, standardFpscr == 1);
}
private static double FPMaxFpscrImpl(double value1, double value2, bool standardFpscr)
{
ExecutionContext context = NativeInterface.GetContext();
FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
@@ -2535,12 +2683,19 @@ namespace ARMeilleure.Instructions
return result;
}
[UnmanagedCallersOnly]
public static double FPMaxNum(double value1, double value2)
{
return FPMaxNumFpscr(value1, value2, false);
return FPMaxNumFpscrImpl(value1, value2, false);
}
public static double FPMaxNumFpscr(double value1, double value2, bool standardFpscr)
[UnmanagedCallersOnly]
public static double FPMaxNumFpscr(double value1, double value2, byte standardFpscr)
{
return FPMaxNumFpscrImpl(value1, value2, standardFpscr == 1);
}
private static double FPMaxNumFpscrImpl(double value1, double value2, bool standardFpscr)
{
ExecutionContext context = NativeInterface.GetContext();
FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
@@ -2557,15 +2712,22 @@ namespace ARMeilleure.Instructions
value2 = FPInfinity(true);
}
return FPMaxFpscr(value1, value2, standardFpscr);
return FPMaxFpscrImpl(value1, value2, standardFpscr);
}
[UnmanagedCallersOnly]
public static double FPMin(double value1, double value2)
{
return FPMinFpscr(value1, value2, false);
return FPMinFpscrImpl(value1, value2, false);
}
public static double FPMinFpscr(double value1, double value2, bool standardFpscr)
[UnmanagedCallersOnly]
public static double FPMinFpscr(double value1, double value2, byte standardFpscr)
{
return FPMinFpscrImpl(value1, value2, standardFpscr == 1);
}
private static double FPMinFpscrImpl(double value1, double value2, bool standardFpscr)
{
ExecutionContext context = NativeInterface.GetContext();
FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
@@ -2626,12 +2788,19 @@ namespace ARMeilleure.Instructions
return result;
}
[UnmanagedCallersOnly]
public static double FPMinNum(double value1, double value2)
{
return FPMinNumFpscr(value1, value2, false);
return FPMinNumFpscrImpl(value1, value2, false);
}
public static double FPMinNumFpscr(double value1, double value2, bool standardFpscr)
[UnmanagedCallersOnly]
public static double FPMinNumFpscr(double value1, double value2, byte standardFpscr)
{
return FPMinNumFpscrImpl(value1, value2, standardFpscr == 1);
}
private static double FPMinNumFpscrImpl(double value1, double value2, bool standardFpscr)
{
ExecutionContext context = NativeInterface.GetContext();
FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
@@ -2648,15 +2817,22 @@ namespace ARMeilleure.Instructions
value2 = FPInfinity(false);
}
return FPMinFpscr(value1, value2, standardFpscr);
return FPMinFpscrImpl(value1, value2, standardFpscr);
}
[UnmanagedCallersOnly]
public static double FPMul(double value1, double value2)
{
return FPMulFpscr(value1, value2, false);
return FPMulFpscrImpl(value1, value2, false);
}
public static double FPMulFpscr(double value1, double value2, bool standardFpscr)
[UnmanagedCallersOnly]
public static double FPMulFpscr(double value1, double value2, byte standardFpscr)
{
return FPMulFpscrImpl(value1, value2, standardFpscr == 1);
}
private static double FPMulFpscrImpl(double value1, double value2, bool standardFpscr)
{
ExecutionContext context = NativeInterface.GetContext();
FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
@@ -2703,12 +2879,19 @@ namespace ARMeilleure.Instructions
return result;
}
[UnmanagedCallersOnly]
public static double FPMulAdd(double valueA, double value1, double value2)
{
return FPMulAddFpscr(valueA, value1, value2, false);
return FPMulAddFpscrImpl(valueA, value1, value2, false);
}
public static double FPMulAddFpscr(double valueA, double value1, double value2, bool standardFpscr)
[UnmanagedCallersOnly]
public static double FPMulAddFpscr(double valueA, double value1, double value2, byte standardFpscr)
{
return FPMulAddFpscrImpl(valueA, value1, value2, standardFpscr == 1);
}
private static double FPMulAddFpscrImpl(double valueA, double value1, double value2, bool standardFpscr)
{
ExecutionContext context = NativeInterface.GetContext();
FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
@@ -2774,20 +2957,23 @@ namespace ARMeilleure.Instructions
return result;
}
[UnmanagedCallersOnly]
public static double FPMulSub(double valueA, double value1, double value2)
{
value1 = value1.FPNeg();
return FPMulAdd(valueA, value1, value2);
return FPMulAddFpscrImpl(valueA, value1, value2, false);
}
public static double FPMulSubFpscr(double valueA, double value1, double value2, bool standardFpscr)
[UnmanagedCallersOnly]
public static double FPMulSubFpscr(double valueA, double value1, double value2, byte standardFpscr)
{
value1 = value1.FPNeg();
return FPMulAddFpscr(valueA, value1, value2, standardFpscr);
return FPMulAddFpscrImpl(valueA, value1, value2, standardFpscr == 1);
}
[UnmanagedCallersOnly]
public static double FPMulX(double value1, double value2)
{
ExecutionContext context = NativeInterface.GetContext();
@@ -2833,27 +3019,36 @@ namespace ARMeilleure.Instructions
return result;
}
[UnmanagedCallersOnly]
public static double FPNegMulAdd(double valueA, double value1, double value2)
{
valueA = valueA.FPNeg();
value1 = value1.FPNeg();
return FPMulAdd(valueA, value1, value2);
return FPMulAddFpscrImpl(valueA, value1, value2, false);
}
[UnmanagedCallersOnly]
public static double FPNegMulSub(double valueA, double value1, double value2)
{
valueA = valueA.FPNeg();
return FPMulAdd(valueA, value1, value2);
return FPMulAddFpscrImpl(valueA, value1, value2, false);
}
[UnmanagedCallersOnly]
public static double FPRecipEstimate(double value)
{
return FPRecipEstimateFpscr(value, false);
return FPRecipEstimateFpscrImpl(value, false);
}
public static double FPRecipEstimateFpscr(double value, bool standardFpscr)
[UnmanagedCallersOnly]
public static double FPRecipEstimateFpscr(double value, byte standardFpscr)
{
return FPRecipEstimateFpscrImpl(value, standardFpscr == 1);
}
private static double FPRecipEstimateFpscrImpl(double value, bool standardFpscr)
{
ExecutionContext context = NativeInterface.GetContext();
FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
@@ -2940,6 +3135,7 @@ namespace ARMeilleure.Instructions
return result;
}
[UnmanagedCallersOnly]
public static double FPRecipStep(double value1, double value2)
{
ExecutionContext context = NativeInterface.GetContext();
@@ -2965,7 +3161,7 @@ namespace ARMeilleure.Instructions
}
else
{
product = FPMulFpscr(value1, value2, true);
product = FPMulFpscrImpl(value1, value2, true);
}
result = FPSubFpscr(FPTwo(false), product, true);
@@ -2974,6 +3170,7 @@ namespace ARMeilleure.Instructions
return result;
}
[UnmanagedCallersOnly]
public static double FPRecipStepFused(double value1, double value2)
{
ExecutionContext context = NativeInterface.GetContext();
@@ -3017,6 +3214,7 @@ namespace ARMeilleure.Instructions
return result;
}
[UnmanagedCallersOnly]
public static double FPRecpX(double value)
{
ExecutionContext context = NativeInterface.GetContext();
@@ -3042,12 +3240,19 @@ namespace ARMeilleure.Instructions
return result;
}
[UnmanagedCallersOnly]
public static double FPRSqrtEstimate(double value)
{
return FPRSqrtEstimateFpscr(value, false);
return FPRSqrtEstimateFpscrImpl(value, false);
}
public static double FPRSqrtEstimateFpscr(double value, bool standardFpscr)
[UnmanagedCallersOnly]
public static double FPRSqrtEstimateFpscr(double value, byte standardFpscr)
{
return FPRSqrtEstimateFpscrImpl(value, standardFpscr == 1);
}
private static double FPRSqrtEstimateFpscrImpl(double value, bool standardFpscr)
{
ExecutionContext context = NativeInterface.GetContext();
FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
@@ -3161,6 +3366,7 @@ namespace ARMeilleure.Instructions
return result;
}
[UnmanagedCallersOnly]
public static double FPRSqrtStep(double value1, double value2)
{
ExecutionContext context = NativeInterface.GetContext();
@@ -3186,7 +3392,7 @@ namespace ARMeilleure.Instructions
}
else
{
product = FPMulFpscr(value1, value2, true);
product = FPMulFpscrImpl(value1, value2, true);
}
result = FPHalvedSub(FPThree(false), product, context, fpcr);
@@ -3195,6 +3401,7 @@ namespace ARMeilleure.Instructions
return result;
}
[UnmanagedCallersOnly]
public static double FPRSqrtStepFused(double value1, double value2)
{
ExecutionContext context = NativeInterface.GetContext();
@@ -3238,6 +3445,7 @@ namespace ARMeilleure.Instructions
return result;
}
[UnmanagedCallersOnly]
public static double FPSqrt(double value)
{
ExecutionContext context = NativeInterface.GetContext();
@@ -3280,6 +3488,7 @@ namespace ARMeilleure.Instructions
return result;
}
[UnmanagedCallersOnly]
public static double FPSub(double value1, double value2)
{
return FPSubFpscr(value1, value2, false);

View File

@@ -7,7 +7,6 @@ namespace ARMeilleure.Memory
public const int DefaultGranularity = 65536; // Mapping granularity in Windows.
public IJitMemoryBlock Block { get; }
public IJitMemoryAllocator Allocator { get; }
public nint Pointer => Block.Pointer;
@@ -22,7 +21,6 @@ namespace ARMeilleure.Memory
granularity = DefaultGranularity;
}
Allocator = allocator;
Block = allocator.Reserve(maxSize);
_maxSize = maxSize;
_sizeGranularity = granularity;

View File

@@ -2,8 +2,6 @@ using ARMeilleure.CodeGen;
using ARMeilleure.CodeGen.Unwinding;
using ARMeilleure.Memory;
using ARMeilleure.Native;
using Humanizer;
using Ryujinx.Common.Logging;
using Ryujinx.Memory;
using System;
using System.Collections.Generic;
@@ -20,68 +18,51 @@ namespace ARMeilleure.Translation.Cache
private static readonly int _pageMask = _pageSize - 1;
private const int CodeAlignment = 4; // Bytes.
private const int CacheSize = 256 * 1024 * 1024;
private const int CacheSize = 2047 * 1024 * 1024;
private static ReservedRegion _jitRegion;
private static JitCacheInvalidation _jitCacheInvalidator;
private static List<CacheMemoryAllocator> _cacheAllocators = [];
private static CacheMemoryAllocator _cacheAllocator;
private static readonly List<CacheEntry> _cacheEntries = [];
private static readonly Lock _lock = new();
private static bool _initialized;
private static readonly List<ReservedRegion> _jitRegions = [];
private static int _activeRegionIndex = 0;
[SupportedOSPlatform("windows")]
[LibraryImport("kernel32.dll", SetLastError = true)]
public static partial nint FlushInstructionCache(nint hProcess, nint lpAddress, nuint dwSize);
public static void Initialize(IJitMemoryAllocator allocator)
{
if (_initialized)
{
return;
}
lock (_lock)
{
if (_initialized)
{
if (OperatingSystem.IsWindows())
{
JitUnwindWindows.RemoveFunctionTableHandler(
_jitRegions[0].Pointer);
}
for (int i = 0; i < _jitRegions.Count; i++)
{
_jitRegions[i].Dispose();
}
_jitRegions.Clear();
_cacheAllocators.Clear();
}
else
{
_initialized = true;
return;
}
_activeRegionIndex = 0;
ReservedRegion firstRegion = new(allocator, CacheSize);
_jitRegions.Add(firstRegion);
CacheMemoryAllocator firstCacheAllocator = new(CacheSize);
_cacheAllocators.Add(firstCacheAllocator);
_jitRegion = new ReservedRegion(allocator, CacheSize);
if (!OperatingSystem.IsWindows() && !OperatingSystem.IsMacOS())
{
_jitCacheInvalidator = new JitCacheInvalidation(allocator);
}
_cacheAllocator = new CacheMemoryAllocator(CacheSize);
if (OperatingSystem.IsWindows())
{
JitUnwindWindows.InstallFunctionTableHandler(
firstRegion.Pointer, CacheSize, firstRegion.Pointer + Allocate(_pageSize)
);
JitUnwindWindows.InstallFunctionTableHandler(_jitRegion.Pointer, CacheSize, _jitRegion.Pointer + Allocate(_pageSize));
}
_initialized = true;
}
}
@@ -94,8 +75,8 @@ namespace ARMeilleure.Translation.Cache
Debug.Assert(_initialized);
int funcOffset = Allocate(code.Length);
ReservedRegion targetRegion = _jitRegions[_activeRegionIndex];
nint funcPtr = targetRegion.Pointer + funcOffset;
nint funcPtr = _jitRegion.Pointer + funcOffset;
if (OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
{
@@ -109,9 +90,9 @@ namespace ARMeilleure.Translation.Cache
}
else
{
ReprotectAsWritable(targetRegion, funcOffset, code.Length);
ReprotectAsWritable(funcOffset, code.Length);
Marshal.Copy(code, 0, funcPtr, code.Length);
ReprotectAsExecutable(targetRegion, funcOffset, code.Length);
ReprotectAsExecutable(funcOffset, code.Length);
if (OperatingSystem.IsWindows() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
{
@@ -135,77 +116,52 @@ namespace ARMeilleure.Translation.Cache
{
Debug.Assert(_initialized);
foreach (ReservedRegion region in _jitRegions)
int funcOffset = (int)(pointer.ToInt64() - _jitRegion.Pointer.ToInt64());
if (TryFind(funcOffset, out CacheEntry entry, out int entryIndex) && entry.Offset == funcOffset)
{
if (pointer.ToInt64() < region.Pointer.ToInt64() ||
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)
{
_cacheAllocators[_activeRegionIndex].Free(funcOffset, AlignCodeSize(entry.Size));
_cacheEntries.RemoveAt(entryIndex);
}
return;
_cacheAllocator.Free(funcOffset, AlignCodeSize(entry.Size));
_cacheEntries.RemoveAt(entryIndex);
}
}
}
private static void ReprotectAsWritable(ReservedRegion region, int offset, int size)
private static void ReprotectAsWritable(int offset, int size)
{
int endOffs = offset + size;
int regionStart = offset & ~_pageMask;
int regionEnd = (endOffs + _pageMask) & ~_pageMask;
region.Block.MapAsRwx((ulong)regionStart, (ulong)(regionEnd - regionStart));
_jitRegion.Block.MapAsRwx((ulong)regionStart, (ulong)(regionEnd - regionStart));
}
private static void ReprotectAsExecutable(ReservedRegion region, int offset, int size)
private static void ReprotectAsExecutable(int offset, int size)
{
int endOffs = offset + size;
int regionStart = offset & ~_pageMask;
int regionEnd = (endOffs + _pageMask) & ~_pageMask;
region.Block.MapAsRx((ulong)regionStart, (ulong)(regionEnd - regionStart));
_jitRegion.Block.MapAsRx((ulong)regionStart, (ulong)(regionEnd - regionStart));
}
private static int Allocate(int codeSize)
{
codeSize = AlignCodeSize(codeSize);
int allocOffset = _cacheAllocators[_activeRegionIndex].Allocate(codeSize);
int allocOffset = _cacheAllocator.Allocate(codeSize);
if (allocOffset >= 0)
if (allocOffset < 0)
{
_jitRegions[_activeRegionIndex].ExpandIfNeeded((ulong)allocOffset + (ulong)codeSize);
return allocOffset;
throw new OutOfMemoryException("JIT Cache exhausted.");
}
int exhaustedRegion = _activeRegionIndex;
ReservedRegion newRegion = new(_jitRegions[0].Allocator, CacheSize);
_jitRegions.Add(newRegion);
_activeRegionIndex = _jitRegions.Count - 1;
_jitRegion.ExpandIfNeeded((ulong)allocOffset + (ulong)codeSize);
Logger.Warning?.Print(LogClass.Cpu, $"JIT Cache Region {exhaustedRegion} exhausted, creating new Cache Region {_activeRegionIndex} ({((long)(_activeRegionIndex + 1) * CacheSize).Bytes()} Total Allocation).");
_cacheAllocators.Add(new CacheMemoryAllocator(CacheSize));
int allocOffsetNew = _cacheAllocators[_activeRegionIndex].Allocate(codeSize);
if (allocOffsetNew < 0)
{
throw new OutOfMemoryException("Failed to allocate in new Cache Region!");
}
newRegion.ExpandIfNeeded((ulong)allocOffsetNew + (ulong)codeSize);
return allocOffsetNew;
return allocOffset;
}
private static int AlignCodeSize(int codeSize)
{
return checked(codeSize + (CodeAlignment - 1)) & ~(CodeAlignment - 1);
@@ -229,21 +185,18 @@ namespace ARMeilleure.Translation.Cache
{
lock (_lock)
{
foreach (ReservedRegion _ in _jitRegions)
int index = _cacheEntries.BinarySearch(new CacheEntry(offset, 0, default));
if (index < 0)
{
int index = _cacheEntries.BinarySearch(new CacheEntry(offset, 0, default));
index = ~index - 1;
}
if (index < 0)
{
index = ~index - 1;
}
if (index >= 0)
{
entry = _cacheEntries[index];
entryIndex = index;
return true;
}
if (index >= 0)
{
entry = _cacheEntries[index];
entryIndex = index;
return true;
}
}

View File

@@ -52,11 +52,6 @@ namespace ARMeilleure.Translation.Cache
nint context,
[MarshalAs(UnmanagedType.LPWStr)] string outOfProcessCallbackDll);
[LibraryImport("kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static unsafe partial bool RtlDeleteFunctionTable(
ulong tableIdentifier);
private static GetRuntimeFunctionCallback _getRuntimeFunctionCallback;
private static int _sizeOfRuntimeFunction;
@@ -96,23 +91,6 @@ namespace ARMeilleure.Translation.Cache
}
}
public static void RemoveFunctionTableHandler(nint codeCachePointer)
{
ulong codeCachePtr = (ulong)codeCachePointer.ToInt64();
bool result;
unsafe
{
result = RtlDeleteFunctionTable(codeCachePtr | 3);
}
if (!result)
{
throw new InvalidOperationException("Failure removing function table callback.");
}
}
private static unsafe RuntimeFunction* FunctionTableHandler(ulong controlPc, nint context)
{
int offset = (int)((long)controlPc - context.ToInt64());

View File

@@ -1,18 +1,10 @@
using System;
namespace ARMeilleure.Translation
{
class DelegateInfo
{
#pragma warning disable IDE0052 // Remove unread private member
private readonly Delegate _dlg; // Ensure that this delegate will not be garbage collected.
#pragma warning restore IDE0052
public nint FuncPtr { get; }
public DelegateInfo(Delegate dlg, nint funcPtr)
public nint FuncPtr { get; private set; }
public DelegateInfo(nint funcPtr)
{
_dlg = dlg;
FuncPtr = funcPtr;
}
}

View File

@@ -1,10 +1,7 @@
using ARMeilleure.Instructions;
using ARMeilleure.State;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.InteropServices;
// ReSharper disable RedundantTypeArgumentsOfMethod
namespace ARMeilleure.Translation
{
@@ -35,21 +32,7 @@ namespace ARMeilleure.Translation
return _delegates.Values[index].FuncPtr; // O(1).
}
public static nint GetDelegateFuncPtr(MethodInfo info)
{
ArgumentNullException.ThrowIfNull(info);
string key = GetKey(info);
if (!_delegates.TryGetValue(key, out DelegateInfo dlgInfo)) // O(log(n)).
{
throw new KeyNotFoundException($"({nameof(key)} = {key})");
}
return dlgInfo.FuncPtr;
}
public static int GetDelegateIndex(MethodInfo info)
{
ArgumentNullException.ThrowIfNull(info);
@@ -65,12 +48,12 @@ namespace ARMeilleure.Translation
return index;
}
private static void SetDelegateInfo(Delegate dlg, nint funcPtr)
private static void SetDelegateInfo(MethodInfo method)
{
string key = GetKey(dlg.Method);
string key = GetKey(method);
_delegates.Add(key, new DelegateInfo(dlg, funcPtr)); // ArgumentException (key).
_delegates.Add(key, new DelegateInfo(method.MethodHandle.GetFunctionPointer())); // ArgumentException (key).
}
private static string GetKey(MethodInfo info)
@@ -84,533 +67,179 @@ namespace ARMeilleure.Translation
{
_delegates = new SortedList<string, DelegateInfo>();
// ReSharper disable InconsistentNaming
// ReSharper disable RedundantDelegateCreation
MathAbs dlgMathAbs = new(Math.Abs);
MathCeiling dlgMathCeiling = new(Math.Ceiling);
MathFloor dlgMathFloor = new(Math.Floor);
MathRound dlgMathRound = new(Math.Round);
MathTruncate dlgMathTruncate = new(Math.Truncate);
SetDelegateInfo(typeof(MathHelper).GetMethod(nameof(MathHelper.Abs)));
SetDelegateInfo(typeof(MathHelper).GetMethod(nameof(MathHelper.Ceiling)));
SetDelegateInfo(typeof(MathHelper).GetMethod(nameof(MathHelper.Floor)));
SetDelegateInfo(typeof(MathHelper).GetMethod(nameof(MathHelper.Round)));
SetDelegateInfo(typeof(MathHelper).GetMethod(nameof(MathHelper.Truncate)));
MathFAbs dlgMathFAbs = new(MathF.Abs);
MathFCeiling dlgMathFCeiling = new(MathF.Ceiling);
MathFFloor dlgMathFFloor = new(MathF.Floor);
MathFRound dlgMathFRound = new(MathF.Round);
MathFTruncate dlgMathFTruncate = new(MathF.Truncate);
SetDelegateInfo(typeof(MathHelperF).GetMethod(nameof(MathHelperF.Abs)));
SetDelegateInfo(typeof(MathHelperF).GetMethod(nameof(MathHelperF.Ceiling)));
SetDelegateInfo(typeof(MathHelperF).GetMethod(nameof(MathHelperF.Floor)));
SetDelegateInfo(typeof(MathHelperF).GetMethod(nameof(MathHelperF.Round)));
SetDelegateInfo(typeof(MathHelperF).GetMethod(nameof(MathHelperF.Truncate)));
NativeInterfaceBreak dlgNativeInterfaceBreak = new(NativeInterface.Break);
NativeInterfaceCheckSynchronization dlgNativeInterfaceCheckSynchronization = new(NativeInterface.CheckSynchronization);
NativeInterfaceEnqueueForRejit dlgNativeInterfaceEnqueueForRejit = new(NativeInterface.EnqueueForRejit);
NativeInterfaceGetCntfrqEl0 dlgNativeInterfaceGetCntfrqEl0 = new(NativeInterface.GetCntfrqEl0);
NativeInterfaceGetCntpctEl0 dlgNativeInterfaceGetCntpctEl0 = new(NativeInterface.GetCntpctEl0);
NativeInterfaceGetCntvctEl0 dlgNativeInterfaceGetCntvctEl0 = new(NativeInterface.GetCntvctEl0);
NativeInterfaceGetCtrEl0 dlgNativeInterfaceGetCtrEl0 = new(NativeInterface.GetCtrEl0);
NativeInterfaceGetDczidEl0 dlgNativeInterfaceGetDczidEl0 = new(NativeInterface.GetDczidEl0);
NativeInterfaceGetFunctionAddress dlgNativeInterfaceGetFunctionAddress = new(NativeInterface.GetFunctionAddress);
NativeInterfaceInvalidateCacheLine dlgNativeInterfaceInvalidateCacheLine = new(NativeInterface.InvalidateCacheLine);
NativeInterfaceReadByte dlgNativeInterfaceReadByte = new(NativeInterface.ReadByte);
NativeInterfaceReadUInt16 dlgNativeInterfaceReadUInt16 = new(NativeInterface.ReadUInt16);
NativeInterfaceReadUInt32 dlgNativeInterfaceReadUInt32 = new(NativeInterface.ReadUInt32);
NativeInterfaceReadUInt64 dlgNativeInterfaceReadUInt64 = new(NativeInterface.ReadUInt64);
NativeInterfaceReadVector128 dlgNativeInterfaceReadVector128 = new(NativeInterface.ReadVector128);
NativeInterfaceSignalMemoryTracking dlgNativeInterfaceSignalMemoryTracking = new(NativeInterface.SignalMemoryTracking);
NativeInterfaceSupervisorCall dlgNativeInterfaceSupervisorCall = new(NativeInterface.SupervisorCall);
NativeInterfaceThrowInvalidMemoryAccess dlgNativeInterfaceThrowInvalidMemoryAccess = new(NativeInterface.ThrowInvalidMemoryAccess);
NativeInterfaceUndefined dlgNativeInterfaceUndefined = new(NativeInterface.Undefined);
NativeInterfaceWriteByte dlgNativeInterfaceWriteByte = new(NativeInterface.WriteByte);
NativeInterfaceWriteUInt16 dlgNativeInterfaceWriteUInt16 = new(NativeInterface.WriteUInt16);
NativeInterfaceWriteUInt32 dlgNativeInterfaceWriteUInt32 = new(NativeInterface.WriteUInt32);
NativeInterfaceWriteUInt64 dlgNativeInterfaceWriteUInt64 = new(NativeInterface.WriteUInt64);
NativeInterfaceWriteVector128 dlgNativeInterfaceWriteVector128 = new(NativeInterface.WriteVector128);
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.Break)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.CheckSynchronization)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.EnqueueForRejit)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntfrqEl0)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntpctEl0)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntvctEl0)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCtrEl0)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetDczidEl0)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFunctionAddress)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.InvalidateCacheLine)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadByte)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt16)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt32)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt64)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadVector128)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SignalMemoryTracking)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SupervisorCall)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ThrowInvalidMemoryAccess)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.Undefined)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteByte)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt16)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt32)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt64)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteVector128)));
SoftFallbackCountLeadingSigns dlgSoftFallbackCountLeadingSigns = new(SoftFallback.CountLeadingSigns);
SoftFallbackCountLeadingZeros dlgSoftFallbackCountLeadingZeros = new(SoftFallback.CountLeadingZeros);
SoftFallbackCrc32b dlgSoftFallbackCrc32b = new(SoftFallback.Crc32b);
SoftFallbackCrc32cb dlgSoftFallbackCrc32cb = new(SoftFallback.Crc32cb);
SoftFallbackCrc32ch dlgSoftFallbackCrc32ch = new(SoftFallback.Crc32ch);
SoftFallbackCrc32cw dlgSoftFallbackCrc32cw = new(SoftFallback.Crc32cw);
SoftFallbackCrc32cx dlgSoftFallbackCrc32cx = new(SoftFallback.Crc32cx);
SoftFallbackCrc32h dlgSoftFallbackCrc32h = new(SoftFallback.Crc32h);
SoftFallbackCrc32w dlgSoftFallbackCrc32w = new(SoftFallback.Crc32w);
SoftFallbackCrc32x dlgSoftFallbackCrc32x = new(SoftFallback.Crc32x);
SoftFallbackDecrypt dlgSoftFallbackDecrypt = new(SoftFallback.Decrypt);
SoftFallbackEncrypt dlgSoftFallbackEncrypt = new(SoftFallback.Encrypt);
SoftFallbackFixedRotate dlgSoftFallbackFixedRotate = new(SoftFallback.FixedRotate);
SoftFallbackHashChoose dlgSoftFallbackHashChoose = new(SoftFallback.HashChoose);
SoftFallbackHashLower dlgSoftFallbackHashLower = new(SoftFallback.HashLower);
SoftFallbackHashMajority dlgSoftFallbackHashMajority = new(SoftFallback.HashMajority);
SoftFallbackHashParity dlgSoftFallbackHashParity = new(SoftFallback.HashParity);
SoftFallbackHashUpper dlgSoftFallbackHashUpper = new(SoftFallback.HashUpper);
SoftFallbackInverseMixColumns dlgSoftFallbackInverseMixColumns = new(SoftFallback.InverseMixColumns);
SoftFallbackMixColumns dlgSoftFallbackMixColumns = new(SoftFallback.MixColumns);
SoftFallbackPolynomialMult64_128 dlgSoftFallbackPolynomialMult64_128 = new(SoftFallback.PolynomialMult64_128);
SoftFallbackSatF32ToS32 dlgSoftFallbackSatF32ToS32 = new(SoftFallback.SatF32ToS32);
SoftFallbackSatF32ToS64 dlgSoftFallbackSatF32ToS64 = new(SoftFallback.SatF32ToS64);
SoftFallbackSatF32ToU32 dlgSoftFallbackSatF32ToU32 = new(SoftFallback.SatF32ToU32);
SoftFallbackSatF32ToU64 dlgSoftFallbackSatF32ToU64 = new(SoftFallback.SatF32ToU64);
SoftFallbackSatF64ToS32 dlgSoftFallbackSatF64ToS32 = new(SoftFallback.SatF64ToS32);
SoftFallbackSatF64ToS64 dlgSoftFallbackSatF64ToS64 = new(SoftFallback.SatF64ToS64);
SoftFallbackSatF64ToU32 dlgSoftFallbackSatF64ToU32 = new(SoftFallback.SatF64ToU32);
SoftFallbackSatF64ToU64 dlgSoftFallbackSatF64ToU64 = new(SoftFallback.SatF64ToU64);
SoftFallbackSha1SchedulePart1 dlgSoftFallbackSha1SchedulePart1 = new(SoftFallback.Sha1SchedulePart1);
SoftFallbackSha1SchedulePart2 dlgSoftFallbackSha1SchedulePart2 = new(SoftFallback.Sha1SchedulePart2);
SoftFallbackSha256SchedulePart1 dlgSoftFallbackSha256SchedulePart1 = new(SoftFallback.Sha256SchedulePart1);
SoftFallbackSha256SchedulePart2 dlgSoftFallbackSha256SchedulePart2 = new(SoftFallback.Sha256SchedulePart2);
SoftFallbackSignedShrImm64 dlgSoftFallbackSignedShrImm64 = new(SoftFallback.SignedShrImm64);
SoftFallbackTbl1 dlgSoftFallbackTbl1 = new(SoftFallback.Tbl1);
SoftFallbackTbl2 dlgSoftFallbackTbl2 = new(SoftFallback.Tbl2);
SoftFallbackTbl3 dlgSoftFallbackTbl3 = new(SoftFallback.Tbl3);
SoftFallbackTbl4 dlgSoftFallbackTbl4 = new(SoftFallback.Tbl4);
SoftFallbackTbx1 dlgSoftFallbackTbx1 = new(SoftFallback.Tbx1);
SoftFallbackTbx2 dlgSoftFallbackTbx2 = new(SoftFallback.Tbx2);
SoftFallbackTbx3 dlgSoftFallbackTbx3 = new(SoftFallback.Tbx3);
SoftFallbackTbx4 dlgSoftFallbackTbx4 = new(SoftFallback.Tbx4);
SoftFallbackUnsignedShrImm64 dlgSoftFallbackUnsignedShrImm64 = new(SoftFallback.UnsignedShrImm64);
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.CountLeadingSigns)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.CountLeadingZeros)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32b)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32cb)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32ch)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32cw)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32cx)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32h)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32w)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Crc32x)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Decrypt)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Encrypt)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.FixedRotate)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.HashChoose)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.HashLower)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.HashMajority)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.HashParity)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.HashUpper)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.InverseMixColumns)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.MixColumns)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.PolynomialMult64_128)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToS32)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToS64)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToU32)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF32ToU64)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF64ToS32)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF64ToS64)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF64ToU32)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SatF64ToU64)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Sha1SchedulePart1)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Sha1SchedulePart2)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Sha256SchedulePart1)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Sha256SchedulePart2)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SignedShrImm64)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbl1)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbl2)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbl3)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbl4)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbx1)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbx2)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbx3)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Tbx4)));
SetDelegateInfo(typeof(SoftFallback).GetMethod(nameof(SoftFallback.UnsignedShrImm64)));
SoftFloat16_32FPConvert dlgSoftFloat16_32FPConvert = new(SoftFloat16_32.FPConvert);
SoftFloat16_64FPConvert dlgSoftFloat16_64FPConvert = new(SoftFloat16_64.FPConvert);
SetDelegateInfo(typeof(SoftFloat16_32).GetMethod(nameof(SoftFloat16_32.FPConvert)));
SetDelegateInfo(typeof(SoftFloat16_64).GetMethod(nameof(SoftFloat16_64.FPConvert)));
SoftFloat32FPAdd dlgSoftFloat32FPAdd = new(SoftFloat32.FPAdd);
SoftFloat32FPAddFpscr dlgSoftFloat32FPAddFpscr = new(SoftFloat32.FPAddFpscr); // A32 only.
SoftFloat32FPCompare dlgSoftFloat32FPCompare = new(SoftFloat32.FPCompare);
SoftFloat32FPCompareEQ dlgSoftFloat32FPCompareEQ = new(SoftFloat32.FPCompareEQ);
SoftFloat32FPCompareEQFpscr dlgSoftFloat32FPCompareEQFpscr = new(SoftFloat32.FPCompareEQFpscr); // A32 only.
SoftFloat32FPCompareGE dlgSoftFloat32FPCompareGE = new(SoftFloat32.FPCompareGE);
SoftFloat32FPCompareGEFpscr dlgSoftFloat32FPCompareGEFpscr = new(SoftFloat32.FPCompareGEFpscr); // A32 only.
SoftFloat32FPCompareGT dlgSoftFloat32FPCompareGT = new(SoftFloat32.FPCompareGT);
SoftFloat32FPCompareGTFpscr dlgSoftFloat32FPCompareGTFpscr = new(SoftFloat32.FPCompareGTFpscr); // A32 only.
SoftFloat32FPCompareLE dlgSoftFloat32FPCompareLE = new(SoftFloat32.FPCompareLE);
SoftFloat32FPCompareLEFpscr dlgSoftFloat32FPCompareLEFpscr = new(SoftFloat32.FPCompareLEFpscr); // A32 only.
SoftFloat32FPCompareLT dlgSoftFloat32FPCompareLT = new(SoftFloat32.FPCompareLT);
SoftFloat32FPCompareLTFpscr dlgSoftFloat32FPCompareLTFpscr = new(SoftFloat32.FPCompareLTFpscr); // A32 only.
SoftFloat32FPDiv dlgSoftFloat32FPDiv = new(SoftFloat32.FPDiv);
SoftFloat32FPMax dlgSoftFloat32FPMax = new(SoftFloat32.FPMax);
SoftFloat32FPMaxFpscr dlgSoftFloat32FPMaxFpscr = new(SoftFloat32.FPMaxFpscr); // A32 only.
SoftFloat32FPMaxNum dlgSoftFloat32FPMaxNum = new(SoftFloat32.FPMaxNum);
SoftFloat32FPMaxNumFpscr dlgSoftFloat32FPMaxNumFpscr = new(SoftFloat32.FPMaxNumFpscr); // A32 only.
SoftFloat32FPMin dlgSoftFloat32FPMin = new(SoftFloat32.FPMin);
SoftFloat32FPMinFpscr dlgSoftFloat32FPMinFpscr = new(SoftFloat32.FPMinFpscr); // A32 only.
SoftFloat32FPMinNum dlgSoftFloat32FPMinNum = new(SoftFloat32.FPMinNum);
SoftFloat32FPMinNumFpscr dlgSoftFloat32FPMinNumFpscr = new(SoftFloat32.FPMinNumFpscr); // A32 only.
SoftFloat32FPMul dlgSoftFloat32FPMul = new(SoftFloat32.FPMul);
SoftFloat32FPMulFpscr dlgSoftFloat32FPMulFpscr = new(SoftFloat32.FPMulFpscr); // A32 only.
SoftFloat32FPMulAdd dlgSoftFloat32FPMulAdd = new(SoftFloat32.FPMulAdd);
SoftFloat32FPMulAddFpscr dlgSoftFloat32FPMulAddFpscr = new(SoftFloat32.FPMulAddFpscr); // A32 only.
SoftFloat32FPMulSub dlgSoftFloat32FPMulSub = new(SoftFloat32.FPMulSub);
SoftFloat32FPMulSubFpscr dlgSoftFloat32FPMulSubFpscr = new(SoftFloat32.FPMulSubFpscr); // A32 only.
SoftFloat32FPMulX dlgSoftFloat32FPMulX = new(SoftFloat32.FPMulX);
SoftFloat32FPNegMulAdd dlgSoftFloat32FPNegMulAdd = new(SoftFloat32.FPNegMulAdd);
SoftFloat32FPNegMulSub dlgSoftFloat32FPNegMulSub = new(SoftFloat32.FPNegMulSub);
SoftFloat32FPRecipEstimate dlgSoftFloat32FPRecipEstimate = new(SoftFloat32.FPRecipEstimate);
SoftFloat32FPRecipEstimateFpscr dlgSoftFloat32FPRecipEstimateFpscr = new(SoftFloat32.FPRecipEstimateFpscr); // A32 only.
SoftFloat32FPRecipStep dlgSoftFloat32FPRecipStep = new(SoftFloat32.FPRecipStep); // A32 only.
SoftFloat32FPRecipStepFused dlgSoftFloat32FPRecipStepFused = new(SoftFloat32.FPRecipStepFused);
SoftFloat32FPRecpX dlgSoftFloat32FPRecpX = new(SoftFloat32.FPRecpX);
SoftFloat32FPRSqrtEstimate dlgSoftFloat32FPRSqrtEstimate = new(SoftFloat32.FPRSqrtEstimate);
SoftFloat32FPRSqrtEstimateFpscr dlgSoftFloat32FPRSqrtEstimateFpscr = new(SoftFloat32.FPRSqrtEstimateFpscr); // A32 only.
SoftFloat32FPRSqrtStep dlgSoftFloat32FPRSqrtStep = new(SoftFloat32.FPRSqrtStep); // A32 only.
SoftFloat32FPRSqrtStepFused dlgSoftFloat32FPRSqrtStepFused = new(SoftFloat32.FPRSqrtStepFused);
SoftFloat32FPSqrt dlgSoftFloat32FPSqrt = new(SoftFloat32.FPSqrt);
SoftFloat32FPSub dlgSoftFloat32FPSub = new(SoftFloat32.FPSub);
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPAdd)));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPAddFpscr))); // A32 only.
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompare)));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareEQ)));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareEQFpscr))); // A32 only.
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareGE)));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareGEFpscr))); // A32 only.
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareGT)));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareGTFpscr))); // A32 only.
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareLE)));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareLEFpscr))); // A32 only.
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareLT)));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareLTFpscr))); // A32 only.
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPDiv)));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMax)));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMaxFpscr))); // A32 only.
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMaxNum)));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMaxNumFpscr))); // A32 only.
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMin)));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMinFpscr))); // A32 only.
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMinNum)));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMinNumFpscr))); // A32 only.
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMul)));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulFpscr))); // A32 only.
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulAdd)));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulAddFpscr))); // A32 only.
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulSub)));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulSubFpscr))); // A32 only.
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulX)));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPNegMulAdd)));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPNegMulSub)));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRecipEstimate)));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRecipEstimateFpscr))); // A32 only.
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRecipStep))); // A32 only.
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRecipStepFused)));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRecpX)));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRSqrtEstimate)));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRSqrtEstimateFpscr))); // A32 only.
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRSqrtStep))); // A32 only.
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRSqrtStepFused)));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPSqrt)));
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPSub)));
SoftFloat32_16FPConvert dlgSoftFloat32_16FPConvert = new(SoftFloat32_16.FPConvert);
SetDelegateInfo(typeof(SoftFloat32_16).GetMethod(nameof(SoftFloat32_16.FPConvert)));
SoftFloat64FPAdd dlgSoftFloat64FPAdd = new(SoftFloat64.FPAdd);
SoftFloat64FPAddFpscr dlgSoftFloat64FPAddFpscr = new(SoftFloat64.FPAddFpscr); // A32 only.
SoftFloat64FPCompare dlgSoftFloat64FPCompare = new(SoftFloat64.FPCompare);
SoftFloat64FPCompareEQ dlgSoftFloat64FPCompareEQ = new(SoftFloat64.FPCompareEQ);
SoftFloat64FPCompareEQFpscr dlgSoftFloat64FPCompareEQFpscr = new(SoftFloat64.FPCompareEQFpscr); // A32 only.
SoftFloat64FPCompareGE dlgSoftFloat64FPCompareGE = new(SoftFloat64.FPCompareGE);
SoftFloat64FPCompareGEFpscr dlgSoftFloat64FPCompareGEFpscr = new(SoftFloat64.FPCompareGEFpscr); // A32 only.
SoftFloat64FPCompareGT dlgSoftFloat64FPCompareGT = new(SoftFloat64.FPCompareGT);
SoftFloat64FPCompareGTFpscr dlgSoftFloat64FPCompareGTFpscr = new(SoftFloat64.FPCompareGTFpscr); // A32 only.
SoftFloat64FPCompareLE dlgSoftFloat64FPCompareLE = new(SoftFloat64.FPCompareLE);
SoftFloat64FPCompareLEFpscr dlgSoftFloat64FPCompareLEFpscr = new(SoftFloat64.FPCompareLEFpscr); // A32 only.
SoftFloat64FPCompareLT dlgSoftFloat64FPCompareLT = new(SoftFloat64.FPCompareLT);
SoftFloat64FPCompareLTFpscr dlgSoftFloat64FPCompareLTFpscr = new(SoftFloat64.FPCompareLTFpscr); // A32 only.
SoftFloat64FPDiv dlgSoftFloat64FPDiv = new(SoftFloat64.FPDiv);
SoftFloat64FPMax dlgSoftFloat64FPMax = new(SoftFloat64.FPMax);
SoftFloat64FPMaxFpscr dlgSoftFloat64FPMaxFpscr = new(SoftFloat64.FPMaxFpscr); // A32 only.
SoftFloat64FPMaxNum dlgSoftFloat64FPMaxNum = new(SoftFloat64.FPMaxNum);
SoftFloat64FPMaxNumFpscr dlgSoftFloat64FPMaxNumFpscr = new(SoftFloat64.FPMaxNumFpscr); // A32 only.
SoftFloat64FPMin dlgSoftFloat64FPMin = new(SoftFloat64.FPMin);
SoftFloat64FPMinFpscr dlgSoftFloat64FPMinFpscr = new(SoftFloat64.FPMinFpscr); // A32 only.
SoftFloat64FPMinNum dlgSoftFloat64FPMinNum = new(SoftFloat64.FPMinNum);
SoftFloat64FPMinNumFpscr dlgSoftFloat64FPMinNumFpscr = new(SoftFloat64.FPMinNumFpscr); // A32 only.
SoftFloat64FPMul dlgSoftFloat64FPMul = new(SoftFloat64.FPMul);
SoftFloat64FPMulFpscr dlgSoftFloat64FPMulFpscr = new(SoftFloat64.FPMulFpscr); // A32 only.
SoftFloat64FPMulAdd dlgSoftFloat64FPMulAdd = new(SoftFloat64.FPMulAdd);
SoftFloat64FPMulAddFpscr dlgSoftFloat64FPMulAddFpscr = new(SoftFloat64.FPMulAddFpscr); // A32 only.
SoftFloat64FPMulSub dlgSoftFloat64FPMulSub = new(SoftFloat64.FPMulSub);
SoftFloat64FPMulSubFpscr dlgSoftFloat64FPMulSubFpscr = new(SoftFloat64.FPMulSubFpscr); // A32 only.
SoftFloat64FPMulX dlgSoftFloat64FPMulX = new(SoftFloat64.FPMulX);
SoftFloat64FPNegMulAdd dlgSoftFloat64FPNegMulAdd = new(SoftFloat64.FPNegMulAdd);
SoftFloat64FPNegMulSub dlgSoftFloat64FPNegMulSub = new(SoftFloat64.FPNegMulSub);
SoftFloat64FPRecipEstimate dlgSoftFloat64FPRecipEstimate = new(SoftFloat64.FPRecipEstimate);
SoftFloat64FPRecipEstimateFpscr dlgSoftFloat64FPRecipEstimateFpscr = new(SoftFloat64.FPRecipEstimateFpscr); // A32 only.
SoftFloat64FPRecipStep dlgSoftFloat64FPRecipStep = new(SoftFloat64.FPRecipStep); // A32 only.
SoftFloat64FPRecipStepFused dlgSoftFloat64FPRecipStepFused = new(SoftFloat64.FPRecipStepFused);
SoftFloat64FPRecpX dlgSoftFloat64FPRecpX = new(SoftFloat64.FPRecpX);
SoftFloat64FPRSqrtEstimate dlgSoftFloat64FPRSqrtEstimate = new(SoftFloat64.FPRSqrtEstimate);
SoftFloat64FPRSqrtEstimateFpscr dlgSoftFloat64FPRSqrtEstimateFpscr = new(SoftFloat64.FPRSqrtEstimateFpscr); // A32 only.
SoftFloat64FPRSqrtStep dlgSoftFloat64FPRSqrtStep = new(SoftFloat64.FPRSqrtStep); // A32 only.
SoftFloat64FPRSqrtStepFused dlgSoftFloat64FPRSqrtStepFused = new(SoftFloat64.FPRSqrtStepFused);
SoftFloat64FPSqrt dlgSoftFloat64FPSqrt = new(SoftFloat64.FPSqrt);
SoftFloat64FPSub dlgSoftFloat64FPSub = new(SoftFloat64.FPSub);
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPAdd)));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPAddFpscr))); // A32 only.
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompare)));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareEQ)));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareEQFpscr))); // A32 only.
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareGE)));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareGEFpscr))); // A32 only.
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareGT)));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareGTFpscr))); // A32 only.
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareLE)));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareLEFpscr))); // A32 only.
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareLT)));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareLTFpscr))); // A32 only.
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPDiv)));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMax)));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMaxFpscr))); // A32 only.
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMaxNum)));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMaxNumFpscr))); // A32 only.
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMin)));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMinFpscr))); // A32 only.
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMinNum)));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMinNumFpscr))); // A32 only.
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMul)));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulFpscr))); // A32 only.
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulAdd)));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulAddFpscr))); // A32 only.
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulSub)));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulSubFpscr))); // A32 only.
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulX)));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPNegMulAdd)));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPNegMulSub)));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRecipEstimate)));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRecipEstimateFpscr))); // A32 only.
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRecipStep))); // A32 only.
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRecipStepFused)));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRecpX)));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRSqrtEstimate)));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRSqrtEstimateFpscr))); // A32 only.
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRSqrtStep))); // A32 only.
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRSqrtStepFused)));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPSqrt)));
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPSub)));
SoftFloat64_16FPConvert dlgSoftFloat64_16FPConvert = new(SoftFloat64_16.FPConvert);
// ReSharper restore InconsistentNaming
// ReSharper restore RedundantDelegateCreation
SetDelegateInfo(dlgMathAbs, Marshal.GetFunctionPointerForDelegate<MathAbs>(dlgMathAbs));
SetDelegateInfo(dlgMathCeiling, Marshal.GetFunctionPointerForDelegate<MathCeiling>(dlgMathCeiling));
SetDelegateInfo(dlgMathFloor, Marshal.GetFunctionPointerForDelegate<MathFloor>(dlgMathFloor));
SetDelegateInfo(dlgMathRound, Marshal.GetFunctionPointerForDelegate<MathRound>(dlgMathRound));
SetDelegateInfo(dlgMathTruncate, Marshal.GetFunctionPointerForDelegate<MathTruncate>(dlgMathTruncate));
SetDelegateInfo(dlgMathFAbs, Marshal.GetFunctionPointerForDelegate<MathFAbs>(dlgMathFAbs));
SetDelegateInfo(dlgMathFCeiling, Marshal.GetFunctionPointerForDelegate<MathFCeiling>(dlgMathFCeiling));
SetDelegateInfo(dlgMathFFloor, Marshal.GetFunctionPointerForDelegate<MathFFloor>(dlgMathFFloor));
SetDelegateInfo(dlgMathFRound, Marshal.GetFunctionPointerForDelegate<MathFRound>(dlgMathFRound));
SetDelegateInfo(dlgMathFTruncate, Marshal.GetFunctionPointerForDelegate<MathFTruncate>(dlgMathFTruncate));
SetDelegateInfo(dlgNativeInterfaceBreak, Marshal.GetFunctionPointerForDelegate<NativeInterfaceBreak>(dlgNativeInterfaceBreak));
SetDelegateInfo(dlgNativeInterfaceCheckSynchronization, Marshal.GetFunctionPointerForDelegate<NativeInterfaceCheckSynchronization>(dlgNativeInterfaceCheckSynchronization));
SetDelegateInfo(dlgNativeInterfaceEnqueueForRejit, Marshal.GetFunctionPointerForDelegate<NativeInterfaceEnqueueForRejit>(dlgNativeInterfaceEnqueueForRejit));
SetDelegateInfo(dlgNativeInterfaceGetCntfrqEl0, Marshal.GetFunctionPointerForDelegate<NativeInterfaceGetCntfrqEl0>(dlgNativeInterfaceGetCntfrqEl0));
SetDelegateInfo(dlgNativeInterfaceGetCntpctEl0, Marshal.GetFunctionPointerForDelegate<NativeInterfaceGetCntpctEl0>(dlgNativeInterfaceGetCntpctEl0));
SetDelegateInfo(dlgNativeInterfaceGetCntvctEl0, Marshal.GetFunctionPointerForDelegate<NativeInterfaceGetCntvctEl0>(dlgNativeInterfaceGetCntvctEl0));
SetDelegateInfo(dlgNativeInterfaceGetCtrEl0, Marshal.GetFunctionPointerForDelegate<NativeInterfaceGetCtrEl0>(dlgNativeInterfaceGetCtrEl0));
SetDelegateInfo(dlgNativeInterfaceGetDczidEl0, Marshal.GetFunctionPointerForDelegate<NativeInterfaceGetDczidEl0>(dlgNativeInterfaceGetDczidEl0));
SetDelegateInfo(dlgNativeInterfaceGetFunctionAddress, Marshal.GetFunctionPointerForDelegate<NativeInterfaceGetFunctionAddress>(dlgNativeInterfaceGetFunctionAddress));
SetDelegateInfo(dlgNativeInterfaceInvalidateCacheLine, Marshal.GetFunctionPointerForDelegate<NativeInterfaceInvalidateCacheLine>(dlgNativeInterfaceInvalidateCacheLine));
SetDelegateInfo(dlgNativeInterfaceReadByte, Marshal.GetFunctionPointerForDelegate<NativeInterfaceReadByte>(dlgNativeInterfaceReadByte));
SetDelegateInfo(dlgNativeInterfaceReadUInt16, Marshal.GetFunctionPointerForDelegate<NativeInterfaceReadUInt16>(dlgNativeInterfaceReadUInt16));
SetDelegateInfo(dlgNativeInterfaceReadUInt32, Marshal.GetFunctionPointerForDelegate<NativeInterfaceReadUInt32>(dlgNativeInterfaceReadUInt32));
SetDelegateInfo(dlgNativeInterfaceReadUInt64, Marshal.GetFunctionPointerForDelegate<NativeInterfaceReadUInt64>(dlgNativeInterfaceReadUInt64));
SetDelegateInfo(dlgNativeInterfaceReadVector128, Marshal.GetFunctionPointerForDelegate<NativeInterfaceReadVector128>(dlgNativeInterfaceReadVector128));
SetDelegateInfo(dlgNativeInterfaceSignalMemoryTracking, Marshal.GetFunctionPointerForDelegate<NativeInterfaceSignalMemoryTracking>(dlgNativeInterfaceSignalMemoryTracking));
SetDelegateInfo(dlgNativeInterfaceSupervisorCall, Marshal.GetFunctionPointerForDelegate<NativeInterfaceSupervisorCall>(dlgNativeInterfaceSupervisorCall));
SetDelegateInfo(dlgNativeInterfaceThrowInvalidMemoryAccess, Marshal.GetFunctionPointerForDelegate<NativeInterfaceThrowInvalidMemoryAccess>(dlgNativeInterfaceThrowInvalidMemoryAccess));
SetDelegateInfo(dlgNativeInterfaceUndefined, Marshal.GetFunctionPointerForDelegate<NativeInterfaceUndefined>(dlgNativeInterfaceUndefined));
SetDelegateInfo(dlgNativeInterfaceWriteByte, Marshal.GetFunctionPointerForDelegate<NativeInterfaceWriteByte>(dlgNativeInterfaceWriteByte));
SetDelegateInfo(dlgNativeInterfaceWriteUInt16, Marshal.GetFunctionPointerForDelegate<NativeInterfaceWriteUInt16>(dlgNativeInterfaceWriteUInt16));
SetDelegateInfo(dlgNativeInterfaceWriteUInt32, Marshal.GetFunctionPointerForDelegate<NativeInterfaceWriteUInt32>(dlgNativeInterfaceWriteUInt32));
SetDelegateInfo(dlgNativeInterfaceWriteUInt64, Marshal.GetFunctionPointerForDelegate<NativeInterfaceWriteUInt64>(dlgNativeInterfaceWriteUInt64));
SetDelegateInfo(dlgNativeInterfaceWriteVector128, Marshal.GetFunctionPointerForDelegate<NativeInterfaceWriteVector128>(dlgNativeInterfaceWriteVector128));
SetDelegateInfo(dlgSoftFallbackCountLeadingSigns, Marshal.GetFunctionPointerForDelegate<SoftFallbackCountLeadingSigns>(dlgSoftFallbackCountLeadingSigns));
SetDelegateInfo(dlgSoftFallbackCountLeadingZeros, Marshal.GetFunctionPointerForDelegate<SoftFallbackCountLeadingZeros>(dlgSoftFallbackCountLeadingZeros));
SetDelegateInfo(dlgSoftFallbackCrc32b, Marshal.GetFunctionPointerForDelegate<SoftFallbackCrc32b>(dlgSoftFallbackCrc32b));
SetDelegateInfo(dlgSoftFallbackCrc32cb, Marshal.GetFunctionPointerForDelegate<SoftFallbackCrc32cb>(dlgSoftFallbackCrc32cb));
SetDelegateInfo(dlgSoftFallbackCrc32ch, Marshal.GetFunctionPointerForDelegate<SoftFallbackCrc32ch>(dlgSoftFallbackCrc32ch));
SetDelegateInfo(dlgSoftFallbackCrc32cw, Marshal.GetFunctionPointerForDelegate<SoftFallbackCrc32cw>(dlgSoftFallbackCrc32cw));
SetDelegateInfo(dlgSoftFallbackCrc32cx, Marshal.GetFunctionPointerForDelegate<SoftFallbackCrc32cx>(dlgSoftFallbackCrc32cx));
SetDelegateInfo(dlgSoftFallbackCrc32h, Marshal.GetFunctionPointerForDelegate<SoftFallbackCrc32h>(dlgSoftFallbackCrc32h));
SetDelegateInfo(dlgSoftFallbackCrc32w, Marshal.GetFunctionPointerForDelegate<SoftFallbackCrc32w>(dlgSoftFallbackCrc32w));
SetDelegateInfo(dlgSoftFallbackCrc32x, Marshal.GetFunctionPointerForDelegate<SoftFallbackCrc32x>(dlgSoftFallbackCrc32x));
SetDelegateInfo(dlgSoftFallbackDecrypt, Marshal.GetFunctionPointerForDelegate<SoftFallbackDecrypt>(dlgSoftFallbackDecrypt));
SetDelegateInfo(dlgSoftFallbackEncrypt, Marshal.GetFunctionPointerForDelegate<SoftFallbackEncrypt>(dlgSoftFallbackEncrypt));
SetDelegateInfo(dlgSoftFallbackFixedRotate, Marshal.GetFunctionPointerForDelegate<SoftFallbackFixedRotate>(dlgSoftFallbackFixedRotate));
SetDelegateInfo(dlgSoftFallbackHashChoose, Marshal.GetFunctionPointerForDelegate<SoftFallbackHashChoose>(dlgSoftFallbackHashChoose));
SetDelegateInfo(dlgSoftFallbackHashLower, Marshal.GetFunctionPointerForDelegate<SoftFallbackHashLower>(dlgSoftFallbackHashLower));
SetDelegateInfo(dlgSoftFallbackHashMajority, Marshal.GetFunctionPointerForDelegate<SoftFallbackHashMajority>(dlgSoftFallbackHashMajority));
SetDelegateInfo(dlgSoftFallbackHashParity, Marshal.GetFunctionPointerForDelegate<SoftFallbackHashParity>(dlgSoftFallbackHashParity));
SetDelegateInfo(dlgSoftFallbackHashUpper, Marshal.GetFunctionPointerForDelegate<SoftFallbackHashUpper>(dlgSoftFallbackHashUpper));
SetDelegateInfo(dlgSoftFallbackInverseMixColumns, Marshal.GetFunctionPointerForDelegate<SoftFallbackInverseMixColumns>(dlgSoftFallbackInverseMixColumns));
SetDelegateInfo(dlgSoftFallbackMixColumns, Marshal.GetFunctionPointerForDelegate<SoftFallbackMixColumns>(dlgSoftFallbackMixColumns));
SetDelegateInfo(dlgSoftFallbackPolynomialMult64_128, Marshal.GetFunctionPointerForDelegate<SoftFallbackPolynomialMult64_128>(dlgSoftFallbackPolynomialMult64_128));
SetDelegateInfo(dlgSoftFallbackSatF32ToS32, Marshal.GetFunctionPointerForDelegate<SoftFallbackSatF32ToS32>(dlgSoftFallbackSatF32ToS32));
SetDelegateInfo(dlgSoftFallbackSatF32ToS64, Marshal.GetFunctionPointerForDelegate<SoftFallbackSatF32ToS64>(dlgSoftFallbackSatF32ToS64));
SetDelegateInfo(dlgSoftFallbackSatF32ToU32, Marshal.GetFunctionPointerForDelegate<SoftFallbackSatF32ToU32>(dlgSoftFallbackSatF32ToU32));
SetDelegateInfo(dlgSoftFallbackSatF32ToU64, Marshal.GetFunctionPointerForDelegate<SoftFallbackSatF32ToU64>(dlgSoftFallbackSatF32ToU64));
SetDelegateInfo(dlgSoftFallbackSatF64ToS32, Marshal.GetFunctionPointerForDelegate<SoftFallbackSatF64ToS32>(dlgSoftFallbackSatF64ToS32));
SetDelegateInfo(dlgSoftFallbackSatF64ToS64, Marshal.GetFunctionPointerForDelegate<SoftFallbackSatF64ToS64>(dlgSoftFallbackSatF64ToS64));
SetDelegateInfo(dlgSoftFallbackSatF64ToU32, Marshal.GetFunctionPointerForDelegate<SoftFallbackSatF64ToU32>(dlgSoftFallbackSatF64ToU32));
SetDelegateInfo(dlgSoftFallbackSatF64ToU64, Marshal.GetFunctionPointerForDelegate<SoftFallbackSatF64ToU64>(dlgSoftFallbackSatF64ToU64));
SetDelegateInfo(dlgSoftFallbackSha1SchedulePart1, Marshal.GetFunctionPointerForDelegate<SoftFallbackSha1SchedulePart1>(dlgSoftFallbackSha1SchedulePart1));
SetDelegateInfo(dlgSoftFallbackSha1SchedulePart2, Marshal.GetFunctionPointerForDelegate<SoftFallbackSha1SchedulePart2>(dlgSoftFallbackSha1SchedulePart2));
SetDelegateInfo(dlgSoftFallbackSha256SchedulePart1, Marshal.GetFunctionPointerForDelegate<SoftFallbackSha256SchedulePart1>(dlgSoftFallbackSha256SchedulePart1));
SetDelegateInfo(dlgSoftFallbackSha256SchedulePart2, Marshal.GetFunctionPointerForDelegate<SoftFallbackSha256SchedulePart2>(dlgSoftFallbackSha256SchedulePart2));
SetDelegateInfo(dlgSoftFallbackSignedShrImm64, Marshal.GetFunctionPointerForDelegate<SoftFallbackSignedShrImm64>(dlgSoftFallbackSignedShrImm64));
SetDelegateInfo(dlgSoftFallbackTbl1, Marshal.GetFunctionPointerForDelegate<SoftFallbackTbl1>(dlgSoftFallbackTbl1));
SetDelegateInfo(dlgSoftFallbackTbl2, Marshal.GetFunctionPointerForDelegate<SoftFallbackTbl2>(dlgSoftFallbackTbl2));
SetDelegateInfo(dlgSoftFallbackTbl3, Marshal.GetFunctionPointerForDelegate<SoftFallbackTbl3>(dlgSoftFallbackTbl3));
SetDelegateInfo(dlgSoftFallbackTbl4, Marshal.GetFunctionPointerForDelegate<SoftFallbackTbl4>(dlgSoftFallbackTbl4));
SetDelegateInfo(dlgSoftFallbackTbx1, Marshal.GetFunctionPointerForDelegate<SoftFallbackTbx1>(dlgSoftFallbackTbx1));
SetDelegateInfo(dlgSoftFallbackTbx2, Marshal.GetFunctionPointerForDelegate<SoftFallbackTbx2>(dlgSoftFallbackTbx2));
SetDelegateInfo(dlgSoftFallbackTbx3, Marshal.GetFunctionPointerForDelegate<SoftFallbackTbx3>(dlgSoftFallbackTbx3));
SetDelegateInfo(dlgSoftFallbackTbx4, Marshal.GetFunctionPointerForDelegate<SoftFallbackTbx4>(dlgSoftFallbackTbx4));
SetDelegateInfo(dlgSoftFallbackUnsignedShrImm64, Marshal.GetFunctionPointerForDelegate<SoftFallbackUnsignedShrImm64>(dlgSoftFallbackUnsignedShrImm64));
SetDelegateInfo(dlgSoftFloat16_32FPConvert, Marshal.GetFunctionPointerForDelegate<SoftFloat16_32FPConvert>(dlgSoftFloat16_32FPConvert));
SetDelegateInfo(dlgSoftFloat16_64FPConvert, Marshal.GetFunctionPointerForDelegate<SoftFloat16_64FPConvert>(dlgSoftFloat16_64FPConvert));
SetDelegateInfo(dlgSoftFloat32FPAdd, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPAdd>(dlgSoftFloat32FPAdd));
SetDelegateInfo(dlgSoftFloat32FPAddFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPAddFpscr>(dlgSoftFloat32FPAddFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat32FPCompare, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPCompare>(dlgSoftFloat32FPCompare));
SetDelegateInfo(dlgSoftFloat32FPCompareEQ, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPCompareEQ>(dlgSoftFloat32FPCompareEQ));
SetDelegateInfo(dlgSoftFloat32FPCompareEQFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPCompareEQFpscr>(dlgSoftFloat32FPCompareEQFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat32FPCompareGE, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPCompareGE>(dlgSoftFloat32FPCompareGE));
SetDelegateInfo(dlgSoftFloat32FPCompareGEFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPCompareGEFpscr>(dlgSoftFloat32FPCompareGEFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat32FPCompareGT, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPCompareGT>(dlgSoftFloat32FPCompareGT));
SetDelegateInfo(dlgSoftFloat32FPCompareGTFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPCompareGTFpscr>(dlgSoftFloat32FPCompareGTFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat32FPCompareLE, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPCompareLE>(dlgSoftFloat32FPCompareLE));
SetDelegateInfo(dlgSoftFloat32FPCompareLEFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPCompareLEFpscr>(dlgSoftFloat32FPCompareLEFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat32FPCompareLT, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPCompareLT>(dlgSoftFloat32FPCompareLT));
SetDelegateInfo(dlgSoftFloat32FPCompareLTFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPCompareLTFpscr>(dlgSoftFloat32FPCompareLTFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat32FPDiv, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPDiv>(dlgSoftFloat32FPDiv));
SetDelegateInfo(dlgSoftFloat32FPMax, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMax>(dlgSoftFloat32FPMax));
SetDelegateInfo(dlgSoftFloat32FPMaxFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMaxFpscr>(dlgSoftFloat32FPMaxFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat32FPMaxNum, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMaxNum>(dlgSoftFloat32FPMaxNum));
SetDelegateInfo(dlgSoftFloat32FPMaxNumFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMaxNumFpscr>(dlgSoftFloat32FPMaxNumFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat32FPMin, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMin>(dlgSoftFloat32FPMin));
SetDelegateInfo(dlgSoftFloat32FPMinFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMinFpscr>(dlgSoftFloat32FPMinFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat32FPMinNum, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMinNum>(dlgSoftFloat32FPMinNum));
SetDelegateInfo(dlgSoftFloat32FPMinNumFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMinNumFpscr>(dlgSoftFloat32FPMinNumFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat32FPMul, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMul>(dlgSoftFloat32FPMul));
SetDelegateInfo(dlgSoftFloat32FPMulFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMulFpscr>(dlgSoftFloat32FPMulFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat32FPMulAdd, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMulAdd>(dlgSoftFloat32FPMulAdd));
SetDelegateInfo(dlgSoftFloat32FPMulAddFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMulAddFpscr>(dlgSoftFloat32FPMulAddFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat32FPMulSub, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMulSub>(dlgSoftFloat32FPMulSub));
SetDelegateInfo(dlgSoftFloat32FPMulSubFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMulSubFpscr>(dlgSoftFloat32FPMulSubFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat32FPMulX, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPMulX>(dlgSoftFloat32FPMulX));
SetDelegateInfo(dlgSoftFloat32FPNegMulAdd, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPNegMulAdd>(dlgSoftFloat32FPNegMulAdd));
SetDelegateInfo(dlgSoftFloat32FPNegMulSub, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPNegMulSub>(dlgSoftFloat32FPNegMulSub));
SetDelegateInfo(dlgSoftFloat32FPRecipEstimate, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPRecipEstimate>(dlgSoftFloat32FPRecipEstimate));
SetDelegateInfo(dlgSoftFloat32FPRecipEstimateFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPRecipEstimateFpscr>(dlgSoftFloat32FPRecipEstimateFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat32FPRecipStep, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPRecipStep>(dlgSoftFloat32FPRecipStep)); // A32 only.
SetDelegateInfo(dlgSoftFloat32FPRecipStepFused, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPRecipStepFused>(dlgSoftFloat32FPRecipStepFused));
SetDelegateInfo(dlgSoftFloat32FPRecpX, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPRecpX>(dlgSoftFloat32FPRecpX));
SetDelegateInfo(dlgSoftFloat32FPRSqrtEstimate, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPRSqrtEstimate>(dlgSoftFloat32FPRSqrtEstimate));
SetDelegateInfo(dlgSoftFloat32FPRSqrtEstimateFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPRSqrtEstimateFpscr>(dlgSoftFloat32FPRSqrtEstimateFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat32FPRSqrtStep, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPRSqrtStep>(dlgSoftFloat32FPRSqrtStep)); // A32 only.
SetDelegateInfo(dlgSoftFloat32FPRSqrtStepFused, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPRSqrtStepFused>(dlgSoftFloat32FPRSqrtStepFused));
SetDelegateInfo(dlgSoftFloat32FPSqrt, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPSqrt>(dlgSoftFloat32FPSqrt));
SetDelegateInfo(dlgSoftFloat32FPSub, Marshal.GetFunctionPointerForDelegate<SoftFloat32FPSub>(dlgSoftFloat32FPSub));
SetDelegateInfo(dlgSoftFloat32_16FPConvert, Marshal.GetFunctionPointerForDelegate<SoftFloat32_16FPConvert>(dlgSoftFloat32_16FPConvert));
SetDelegateInfo(dlgSoftFloat64FPAdd, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPAdd>(dlgSoftFloat64FPAdd));
SetDelegateInfo(dlgSoftFloat64FPAddFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPAddFpscr>(dlgSoftFloat64FPAddFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat64FPCompare, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPCompare>(dlgSoftFloat64FPCompare));
SetDelegateInfo(dlgSoftFloat64FPCompareEQ, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPCompareEQ>(dlgSoftFloat64FPCompareEQ));
SetDelegateInfo(dlgSoftFloat64FPCompareEQFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPCompareEQFpscr>(dlgSoftFloat64FPCompareEQFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat64FPCompareGE, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPCompareGE>(dlgSoftFloat64FPCompareGE));
SetDelegateInfo(dlgSoftFloat64FPCompareGEFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPCompareGEFpscr>(dlgSoftFloat64FPCompareGEFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat64FPCompareGT, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPCompareGT>(dlgSoftFloat64FPCompareGT));
SetDelegateInfo(dlgSoftFloat64FPCompareGTFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPCompareGTFpscr>(dlgSoftFloat64FPCompareGTFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat64FPCompareLE, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPCompareLE>(dlgSoftFloat64FPCompareLE));
SetDelegateInfo(dlgSoftFloat64FPCompareLEFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPCompareLEFpscr>(dlgSoftFloat64FPCompareLEFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat64FPCompareLT, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPCompareLT>(dlgSoftFloat64FPCompareLT));
SetDelegateInfo(dlgSoftFloat64FPCompareLTFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPCompareLTFpscr>(dlgSoftFloat64FPCompareLTFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat64FPDiv, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPDiv>(dlgSoftFloat64FPDiv));
SetDelegateInfo(dlgSoftFloat64FPMax, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMax>(dlgSoftFloat64FPMax));
SetDelegateInfo(dlgSoftFloat64FPMaxFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMaxFpscr>(dlgSoftFloat64FPMaxFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat64FPMaxNum, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMaxNum>(dlgSoftFloat64FPMaxNum));
SetDelegateInfo(dlgSoftFloat64FPMaxNumFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMaxNumFpscr>(dlgSoftFloat64FPMaxNumFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat64FPMin, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMin>(dlgSoftFloat64FPMin));
SetDelegateInfo(dlgSoftFloat64FPMinFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMinFpscr>(dlgSoftFloat64FPMinFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat64FPMinNum, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMinNum>(dlgSoftFloat64FPMinNum));
SetDelegateInfo(dlgSoftFloat64FPMinNumFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMinNumFpscr>(dlgSoftFloat64FPMinNumFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat64FPMul, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMul>(dlgSoftFloat64FPMul));
SetDelegateInfo(dlgSoftFloat64FPMulFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMulFpscr>(dlgSoftFloat64FPMulFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat64FPMulAdd, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMulAdd>(dlgSoftFloat64FPMulAdd));
SetDelegateInfo(dlgSoftFloat64FPMulAddFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMulAddFpscr>(dlgSoftFloat64FPMulAddFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat64FPMulSub, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMulSub>(dlgSoftFloat64FPMulSub));
SetDelegateInfo(dlgSoftFloat64FPMulSubFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMulSubFpscr>(dlgSoftFloat64FPMulSubFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat64FPMulX, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPMulX>(dlgSoftFloat64FPMulX));
SetDelegateInfo(dlgSoftFloat64FPNegMulAdd, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPNegMulAdd>(dlgSoftFloat64FPNegMulAdd));
SetDelegateInfo(dlgSoftFloat64FPNegMulSub, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPNegMulSub>(dlgSoftFloat64FPNegMulSub));
SetDelegateInfo(dlgSoftFloat64FPRecipEstimate, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPRecipEstimate>(dlgSoftFloat64FPRecipEstimate));
SetDelegateInfo(dlgSoftFloat64FPRecipEstimateFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPRecipEstimateFpscr>(dlgSoftFloat64FPRecipEstimateFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat64FPRecipStep, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPRecipStep>(dlgSoftFloat64FPRecipStep)); // A32 only.
SetDelegateInfo(dlgSoftFloat64FPRecipStepFused, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPRecipStepFused>(dlgSoftFloat64FPRecipStepFused));
SetDelegateInfo(dlgSoftFloat64FPRecpX, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPRecpX>(dlgSoftFloat64FPRecpX));
SetDelegateInfo(dlgSoftFloat64FPRSqrtEstimate, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPRSqrtEstimate>(dlgSoftFloat64FPRSqrtEstimate));
SetDelegateInfo(dlgSoftFloat64FPRSqrtEstimateFpscr, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPRSqrtEstimateFpscr>(dlgSoftFloat64FPRSqrtEstimateFpscr)); // A32 only.
SetDelegateInfo(dlgSoftFloat64FPRSqrtStep, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPRSqrtStep>(dlgSoftFloat64FPRSqrtStep)); // A32 only.
SetDelegateInfo(dlgSoftFloat64FPRSqrtStepFused, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPRSqrtStepFused>(dlgSoftFloat64FPRSqrtStepFused));
SetDelegateInfo(dlgSoftFloat64FPSqrt, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPSqrt>(dlgSoftFloat64FPSqrt));
SetDelegateInfo(dlgSoftFloat64FPSub, Marshal.GetFunctionPointerForDelegate<SoftFloat64FPSub>(dlgSoftFloat64FPSub));
SetDelegateInfo(dlgSoftFloat64_16FPConvert, Marshal.GetFunctionPointerForDelegate<SoftFloat64_16FPConvert>(dlgSoftFloat64_16FPConvert));
SetDelegateInfo(typeof(SoftFloat64_16).GetMethod(nameof(SoftFloat64_16.FPConvert)));
}
private delegate double MathAbs(double value);
private delegate double MathCeiling(double a);
private delegate double MathFloor(double d);
private delegate double MathRound(double value, MidpointRounding mode);
private delegate double MathTruncate(double d);
private delegate float MathFAbs(float x);
private delegate float MathFCeiling(float x);
private delegate float MathFFloor(float x);
private delegate float MathFRound(float x, MidpointRounding mode);
private delegate float MathFTruncate(float x);
private delegate void NativeInterfaceBreak(ulong address, int imm);
private delegate bool NativeInterfaceCheckSynchronization();
private delegate void NativeInterfaceEnqueueForRejit(ulong address);
private delegate ulong NativeInterfaceGetCntfrqEl0();
private delegate ulong NativeInterfaceGetCntpctEl0();
private delegate ulong NativeInterfaceGetCntvctEl0();
private delegate ulong NativeInterfaceGetCtrEl0();
private delegate ulong NativeInterfaceGetDczidEl0();
private delegate ulong NativeInterfaceGetFunctionAddress(ulong address);
private delegate void NativeInterfaceInvalidateCacheLine(ulong address);
private delegate byte NativeInterfaceReadByte(ulong address);
private delegate ushort NativeInterfaceReadUInt16(ulong address);
private delegate uint NativeInterfaceReadUInt32(ulong address);
private delegate ulong NativeInterfaceReadUInt64(ulong address);
private delegate V128 NativeInterfaceReadVector128(ulong address);
private delegate void NativeInterfaceSignalMemoryTracking(ulong address, ulong size, bool write);
private delegate void NativeInterfaceSupervisorCall(ulong address, int imm);
private delegate void NativeInterfaceThrowInvalidMemoryAccess(ulong address);
private delegate void NativeInterfaceUndefined(ulong address, int opCode);
private delegate void NativeInterfaceWriteByte(ulong address, byte value);
private delegate void NativeInterfaceWriteUInt16(ulong address, ushort value);
private delegate void NativeInterfaceWriteUInt32(ulong address, uint value);
private delegate void NativeInterfaceWriteUInt64(ulong address, ulong value);
private delegate void NativeInterfaceWriteVector128(ulong address, V128 value);
private delegate ulong SoftFallbackCountLeadingSigns(ulong value, int size);
private delegate ulong SoftFallbackCountLeadingZeros(ulong value, int size);
private delegate uint SoftFallbackCrc32b(uint crc, byte value);
private delegate uint SoftFallbackCrc32cb(uint crc, byte value);
private delegate uint SoftFallbackCrc32ch(uint crc, ushort value);
private delegate uint SoftFallbackCrc32cw(uint crc, uint value);
private delegate uint SoftFallbackCrc32cx(uint crc, ulong value);
private delegate uint SoftFallbackCrc32h(uint crc, ushort value);
private delegate uint SoftFallbackCrc32w(uint crc, uint value);
private delegate uint SoftFallbackCrc32x(uint crc, ulong value);
private delegate V128 SoftFallbackDecrypt(V128 value, V128 roundKey);
private delegate V128 SoftFallbackEncrypt(V128 value, V128 roundKey);
private delegate uint SoftFallbackFixedRotate(uint hash_e);
private delegate V128 SoftFallbackHashChoose(V128 hash_abcd, uint hash_e, V128 wk);
private delegate V128 SoftFallbackHashLower(V128 hash_abcd, V128 hash_efgh, V128 wk);
private delegate V128 SoftFallbackHashMajority(V128 hash_abcd, uint hash_e, V128 wk);
private delegate V128 SoftFallbackHashParity(V128 hash_abcd, uint hash_e, V128 wk);
private delegate V128 SoftFallbackHashUpper(V128 hash_abcd, V128 hash_efgh, V128 wk);
private delegate V128 SoftFallbackInverseMixColumns(V128 value);
private delegate V128 SoftFallbackMixColumns(V128 value);
private delegate V128 SoftFallbackPolynomialMult64_128(ulong op1, ulong op2);
private delegate int SoftFallbackSatF32ToS32(float value);
private delegate long SoftFallbackSatF32ToS64(float value);
private delegate uint SoftFallbackSatF32ToU32(float value);
private delegate ulong SoftFallbackSatF32ToU64(float value);
private delegate int SoftFallbackSatF64ToS32(double value);
private delegate long SoftFallbackSatF64ToS64(double value);
private delegate uint SoftFallbackSatF64ToU32(double value);
private delegate ulong SoftFallbackSatF64ToU64(double value);
private delegate V128 SoftFallbackSha1SchedulePart1(V128 w0_3, V128 w4_7, V128 w8_11);
private delegate V128 SoftFallbackSha1SchedulePart2(V128 tw0_3, V128 w12_15);
private delegate V128 SoftFallbackSha256SchedulePart1(V128 w0_3, V128 w4_7);
private delegate V128 SoftFallbackSha256SchedulePart2(V128 w0_3, V128 w8_11, V128 w12_15);
private delegate long SoftFallbackSignedShrImm64(long value, long roundConst, int shift);
private delegate V128 SoftFallbackTbl1(V128 vector, int bytes, V128 tb0);
private delegate V128 SoftFallbackTbl2(V128 vector, int bytes, V128 tb0, V128 tb1);
private delegate V128 SoftFallbackTbl3(V128 vector, int bytes, V128 tb0, V128 tb1, V128 tb2);
private delegate V128 SoftFallbackTbl4(V128 vector, int bytes, V128 tb0, V128 tb1, V128 tb2, V128 tb3);
private delegate V128 SoftFallbackTbx1(V128 dest, V128 vector, int bytes, V128 tb0);
private delegate V128 SoftFallbackTbx2(V128 dest, V128 vector, int bytes, V128 tb0, V128 tb1);
private delegate V128 SoftFallbackTbx3(V128 dest, V128 vector, int bytes, V128 tb0, V128 tb1, V128 tb2);
private delegate V128 SoftFallbackTbx4(V128 dest, V128 vector, int bytes, V128 tb0, V128 tb1, V128 tb2, V128 tb3);
private delegate ulong SoftFallbackUnsignedShrImm64(ulong value, long roundConst, int shift);
private delegate float SoftFloat16_32FPConvert(ushort valueBits);
private delegate double SoftFloat16_64FPConvert(ushort valueBits);
private delegate float SoftFloat32FPAdd(float value1, float value2);
private delegate float SoftFloat32FPAddFpscr(float value1, float value2, bool standardFpscr);
private delegate int SoftFloat32FPCompare(float value1, float value2, bool signalNaNs);
private delegate float SoftFloat32FPCompareEQ(float value1, float value2);
private delegate float SoftFloat32FPCompareEQFpscr(float value1, float value2, bool standardFpscr);
private delegate float SoftFloat32FPCompareGE(float value1, float value2);
private delegate float SoftFloat32FPCompareGEFpscr(float value1, float value2, bool standardFpscr);
private delegate float SoftFloat32FPCompareGT(float value1, float value2);
private delegate float SoftFloat32FPCompareGTFpscr(float value1, float value2, bool standardFpscr);
private delegate float SoftFloat32FPCompareLE(float value1, float value2);
private delegate float SoftFloat32FPCompareLEFpscr(float value1, float value2, bool standardFpscr);
private delegate float SoftFloat32FPCompareLT(float value1, float value2);
private delegate float SoftFloat32FPCompareLTFpscr(float value1, float value2, bool standardFpscr);
private delegate float SoftFloat32FPDiv(float value1, float value2);
private delegate float SoftFloat32FPMax(float value1, float value2);
private delegate float SoftFloat32FPMaxFpscr(float value1, float value2, bool standardFpscr);
private delegate float SoftFloat32FPMaxNum(float value1, float value2);
private delegate float SoftFloat32FPMaxNumFpscr(float value1, float value2, bool standardFpscr);
private delegate float SoftFloat32FPMin(float value1, float value2);
private delegate float SoftFloat32FPMinFpscr(float value1, float value2, bool standardFpscr);
private delegate float SoftFloat32FPMinNum(float value1, float value2);
private delegate float SoftFloat32FPMinNumFpscr(float value1, float value2, bool standardFpscr);
private delegate float SoftFloat32FPMul(float value1, float value2);
private delegate float SoftFloat32FPMulFpscr(float value1, float value2, bool standardFpscr);
private delegate float SoftFloat32FPMulAdd(float valueA, float value1, float value2);
private delegate float SoftFloat32FPMulAddFpscr(float valueA, float value1, float value2, bool standardFpscr);
private delegate float SoftFloat32FPMulSub(float valueA, float value1, float value2);
private delegate float SoftFloat32FPMulSubFpscr(float valueA, float value1, float value2, bool standardFpscr);
private delegate float SoftFloat32FPMulX(float value1, float value2);
private delegate float SoftFloat32FPNegMulAdd(float valueA, float value1, float value2);
private delegate float SoftFloat32FPNegMulSub(float valueA, float value1, float value2);
private delegate float SoftFloat32FPRecipEstimate(float value);
private delegate float SoftFloat32FPRecipEstimateFpscr(float value, bool standardFpscr);
private delegate float SoftFloat32FPRecipStep(float value1, float value2);
private delegate float SoftFloat32FPRecipStepFused(float value1, float value2);
private delegate float SoftFloat32FPRecpX(float value);
private delegate float SoftFloat32FPRSqrtEstimate(float value);
private delegate float SoftFloat32FPRSqrtEstimateFpscr(float value, bool standardFpscr);
private delegate float SoftFloat32FPRSqrtStep(float value1, float value2);
private delegate float SoftFloat32FPRSqrtStepFused(float value1, float value2);
private delegate float SoftFloat32FPSqrt(float value);
private delegate float SoftFloat32FPSub(float value1, float value2);
private delegate ushort SoftFloat32_16FPConvert(float value);
private delegate double SoftFloat64FPAdd(double value1, double value2);
private delegate double SoftFloat64FPAddFpscr(double value1, double value2, bool standardFpscr);
private delegate int SoftFloat64FPCompare(double value1, double value2, bool signalNaNs);
private delegate double SoftFloat64FPCompareEQ(double value1, double value2);
private delegate double SoftFloat64FPCompareEQFpscr(double value1, double value2, bool standardFpscr);
private delegate double SoftFloat64FPCompareGE(double value1, double value2);
private delegate double SoftFloat64FPCompareGEFpscr(double value1, double value2, bool standardFpscr);
private delegate double SoftFloat64FPCompareGT(double value1, double value2);
private delegate double SoftFloat64FPCompareGTFpscr(double value1, double value2, bool standardFpscr);
private delegate double SoftFloat64FPCompareLE(double value1, double value2);
private delegate double SoftFloat64FPCompareLEFpscr(double value1, double value2, bool standardFpscr);
private delegate double SoftFloat64FPCompareLT(double value1, double value2);
private delegate double SoftFloat64FPCompareLTFpscr(double value1, double value2, bool standardFpscr);
private delegate double SoftFloat64FPDiv(double value1, double value2);
private delegate double SoftFloat64FPMax(double value1, double value2);
private delegate double SoftFloat64FPMaxFpscr(double value1, double value2, bool standardFpscr);
private delegate double SoftFloat64FPMaxNum(double value1, double value2);
private delegate double SoftFloat64FPMaxNumFpscr(double value1, double value2, bool standardFpscr);
private delegate double SoftFloat64FPMin(double value1, double value2);
private delegate double SoftFloat64FPMinFpscr(double value1, double value2, bool standardFpscr);
private delegate double SoftFloat64FPMinNum(double value1, double value2);
private delegate double SoftFloat64FPMinNumFpscr(double value1, double value2, bool standardFpscr);
private delegate double SoftFloat64FPMul(double value1, double value2);
private delegate double SoftFloat64FPMulFpscr(double value1, double value2, bool standardFpscr);
private delegate double SoftFloat64FPMulAdd(double valueA, double value1, double value2);
private delegate double SoftFloat64FPMulAddFpscr(double valueA, double value1, double value2, bool standardFpscr);
private delegate double SoftFloat64FPMulSub(double valueA, double value1, double value2);
private delegate double SoftFloat64FPMulSubFpscr(double valueA, double value1, double value2, bool standardFpscr);
private delegate double SoftFloat64FPMulX(double value1, double value2);
private delegate double SoftFloat64FPNegMulAdd(double valueA, double value1, double value2);
private delegate double SoftFloat64FPNegMulSub(double valueA, double value1, double value2);
private delegate double SoftFloat64FPRecipEstimate(double value);
private delegate double SoftFloat64FPRecipEstimateFpscr(double value, bool standardFpscr);
private delegate double SoftFloat64FPRecipStep(double value1, double value2);
private delegate double SoftFloat64FPRecipStepFused(double value1, double value2);
private delegate double SoftFloat64FPRecpX(double value);
private delegate double SoftFloat64FPRSqrtEstimate(double value);
private delegate double SoftFloat64FPRSqrtEstimateFpscr(double value, bool standardFpscr);
private delegate double SoftFloat64FPRSqrtStep(double value1, double value2);
private delegate double SoftFloat64FPRSqrtStepFused(double value1, double value2);
private delegate double SoftFloat64FPSqrt(double value);
private delegate double SoftFloat64FPSub(double value1, double value2);
private delegate ushort SoftFloat64_16FPConvert(double value);
}
}

View File

@@ -97,7 +97,7 @@ namespace ARMeilleure.Translation
public virtual Operand Call(MethodInfo info, params Operand[] callArgs)
{
nint funcPtr = Delegates.GetDelegateFuncPtr(info);
nint funcPtr = info.MethodHandle.GetFunctionPointer();
OperandType returnType = GetOperandType(info.ReturnType);

View File

@@ -144,15 +144,17 @@ namespace ARMeilleure.Translation.PTC
public List<ulong> GetBlacklistedFunctions()
{
List<ulong> funcs = [];
List<ulong> funcs = new List<ulong>();
foreach ((ulong ptr, FuncProfile funcProfile) in ProfiledFuncs)
foreach (var profiledFunc in ProfiledFuncs)
{
if (!funcProfile.Blacklist)
continue;
if (!funcs.Contains(ptr))
funcs.Add(ptr);
if (profiledFunc.Value.Blacklist)
{
if (!funcs.Contains(profiledFunc.Key))
{
funcs.Add(profiledFunc.Key);
}
}
}
return funcs;

View File

@@ -457,7 +457,7 @@ namespace ARMeilleure.Translation
context.Store(address, count);
context.BranchIf(lblEnd, curCount, Const(MinsCallForRejit), Comparison.NotEqual, BasicBlockFrequency.Cold);
context.Call(NativeInterface.Type.GetMethod(nameof(NativeInterface.EnqueueForRejit)), Const(context.EntryAddress));
context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.EnqueueForRejit)), Const(context.EntryAddress));
context.MarkLabel(lblEnd);
}
@@ -473,7 +473,7 @@ namespace ARMeilleure.Translation
Operand count = context.Load(OperandType.I32, countAddr);
context.BranchIfTrue(lblNonZero, count, BasicBlockFrequency.Cold);
Operand running = context.Call(NativeInterface.Type.GetMethod(nameof(NativeInterface.CheckSynchronization)));
Operand running = context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.CheckSynchronization)));
context.BranchIfTrue(lblExit, running, BasicBlockFrequency.Cold);
context.Return(Const(0L));

View File

@@ -4,7 +4,6 @@ using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.State;
using ARMeilleure.Translation.Cache;
using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
@@ -182,7 +181,7 @@ namespace ARMeilleure.Translation
context.Tailcall(hostAddress, nativeContext);
context.MarkLabel(lblFallback);
hostAddress = context.Call(NativeInterface.Type.GetMethod(nameof(NativeInterface.GetFunctionAddress)), guestAddress);
hostAddress = context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFunctionAddress)), guestAddress);
context.Tailcall(hostAddress, nativeContext);
ControlFlowGraph cfg = context.GetControlFlowGraph();
@@ -207,7 +206,7 @@ namespace ARMeilleure.Translation
Operand guestAddress = context.Load(OperandType.I64,
context.Add(nativeContext, Const((ulong)NativeContext.GetDispatchAddressOffset())));
Operand hostAddress = context.Call(NativeInterface.Type.GetMethod(nameof(NativeInterface.GetFunctionAddress)), guestAddress);
Operand hostAddress = context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFunctionAddress)), guestAddress);
context.Tailcall(hostAddress, nativeContext);
ControlFlowGraph cfg = context.GetControlFlowGraph();

View File

@@ -2,7 +2,6 @@
<PropertyGroup>
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
<Configurations>Debug;Release;DebugAOT;ReleaseAOT</Configurations>
</PropertyGroup>
<ItemGroup>

View File

@@ -3,7 +3,6 @@
<PropertyGroup>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
<Configurations>Debug;Release;DebugAOT;ReleaseAOT</Configurations>
</PropertyGroup>
<ItemGroup>

View File

@@ -4,7 +4,6 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<RuntimeIdentifiers>win-x64;osx-x64;linux-x64</RuntimeIdentifiers>
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
<Configurations>Debug;Release;DebugAOT;ReleaseAOT</Configurations>
</PropertyGroup>
<ItemGroup>

View File

@@ -3,7 +3,6 @@
<PropertyGroup>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
<Configurations>Debug;Release;DebugAOT;ReleaseAOT</Configurations>
</PropertyGroup>
<ItemGroup>

View File

@@ -6,7 +6,9 @@ namespace Ryujinx.Common.Configuration
[JsonConverter(typeof(TypedStringEnumConverter<GraphicsBackend>))]
public enum GraphicsBackend
{
Auto,
Vulkan,
OpenGl,
Metal
}
}

View File

@@ -5,34 +5,15 @@ using System.Runtime.InteropServices;
namespace Ryujinx.Common.Helper
{
public enum OperatingSystemType
{
MacOS,
Linux,
Windows
}
public static class RunningPlatform
{
public static readonly OperatingSystemType CurrentOS
= IsMacOS
? OperatingSystemType.MacOS
: IsWindows
? OperatingSystemType.Windows
: IsLinux
? OperatingSystemType.Linux
: throw new PlatformNotSupportedException();
public static Architecture Architecture => RuntimeInformation.OSArchitecture;
public static Architecture CurrentProcessArchitecture => RuntimeInformation.ProcessArchitecture;
public static bool IsMacOS => OperatingSystem.IsMacOS();
public static bool IsWindows => OperatingSystem.IsWindows();
public static bool IsLinux => OperatingSystem.IsLinux();
public static bool IsArm => Architecture is Architecture.Arm64;
public static bool IsArm => RuntimeInformation.OSArchitecture is Architecture.Arm64;
public static bool IsX64 => Architecture is Architecture.X64;
public static bool IsX64 => RuntimeInformation.OSArchitecture is Architecture.X64;
public static bool IsIntelMac => IsMacOS && IsX64;
public static bool IsArmMac => IsMacOS && IsArm;

View File

@@ -4,7 +4,6 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DefineConstants Condition=" '$(ExtraDefineConstants)' != '' ">$(DefineConstants);$(ExtraDefineConstants)</DefineConstants>
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
<Configurations>Debug;Release;DebugAOT;ReleaseAOT</Configurations>
</PropertyGroup>
<ItemGroup>

View File

@@ -1,9 +0,0 @@
namespace Ryujinx.Common
{
public static class SharedConstants
{
public const string DefaultLanPlayHost = "ryuldn.vudjun.com";
public const short LanPlayPort = 30456;
public const string DefaultLanPlayWebHost = "ryuldnweb.vudjun.com";
}
}

View File

@@ -10,6 +10,55 @@ namespace Ryujinx.Common
{
public static ReactiveObject<Optional<string>> CurrentApplication { get; } = new();
public static GraphicsBackend SelectGraphicsBackend(string titleId, GraphicsBackend currentBackend)
{
switch (currentBackend)
{
case GraphicsBackend.Metal when !OperatingSystem.IsMacOS():
case GraphicsBackend.OpenGl when OperatingSystem.IsMacOS():
return GraphicsBackend.Vulkan;
case GraphicsBackend.Vulkan or GraphicsBackend.OpenGl or GraphicsBackend.Metal:
return currentBackend;
}
if (!RunningPlatform.IsArmMac)
return GraphicsBackend.Vulkan;
return GreatMetalTitles.ContainsIgnoreCase(titleId) ? GraphicsBackend.Metal : GraphicsBackend.Vulkan;
}
public static readonly string[] GreatMetalTitles =
[
"01009b500007c000", // ARMS
"0100a5c00d162000", // Cuphead
"010023800d64a000", // Deltarune
"01003a30012c0000", // LEGO City Undercover
"010048701995e000", // Luigi's Manion 2 HD
"010028600EBDA000", // Mario 3D World
"0100152000022000", // Mario Kart 8 Deluxe
"010075a016a3a000", // Persona 4 Arena Ultimax
"0100187003A36000", // Pokémon: Let's Go, Eevee!
"010003f003a34000", // Pokémon: Let's Go, Pikachu!
"01008C0016544000", // Sea of Stars
"01006A800016E000", // Smash Ultimate
"01006bb00c6f0000", // The Legend of Zelda: Link's Awakening
// These ones have small issues, but those happen on Vulkan as well:
"01006f8002326000", // Animal Crossings: New Horizons
"01009bf0072d4000", // Captain Toad: Treasure Tracker
"01009510001ca000", // Fast RMX
"01005CA01580E000", // Persona 5 Royal
"0100b880154fc000", // Persona 5 The Royal (Japan)
"010015100b514000", // Super Mario Bros. Wonder
"0100000000010000", // Super Mario Odyssey
// Further testing is appreciated, I did not test the entire game:
"01007300020fa000", // Astral Chain
"010076f0049a2000", // Bayonetta
"0100cf5010fec000", // Bayonetta Origins: Cereza and the Lost Demon
"0100f4300bf2c000", // New Pokemon Snap
];
public static string GetDiscordGameAsset(string titleId)
=> DiscordGameAssetKeys.Contains(titleId) ? titleId : "game";
@@ -115,16 +164,15 @@ namespace Ryujinx.Common
"0100ba0018500000", // Splatoon 3: Splatfest World Premiere
//NSO Membership games
"0100ccf019c8c000", // F-ZERO 99
"0100c62011050000", // GB - Nintendo Switch Online
"010012f017576000", // GBA - Nintendo Switch Online
"0100c9a00ece6000", // N64 - Nintendo Switch Online
"0100e0601c632000", // N64 - Nintendo Switch Online 18+
"0100d870045b6000", // NES - Nintendo Switch Online
"0100b3c014bda000", // SEGA Genesis - Nintendo Switch Online
"01008d300c50c000", // SNES - Nintendo Switch Online
"0100ccf019c8c000", // F-ZERO 99
"0100ad9012510000", // PAC-MAN 99
"010040600c5ce000", // Tetris 99
"01008d300c50c000", // SNES - Nintendo Switch Online
"0100277011f1a000", // Super Mario Bros. 35
//Misc Nintendo 1st party games
@@ -170,7 +218,6 @@ namespace Ryujinx.Common
//Misc Games
"010056e00853a000", // A Hat in Time
"0100fd1014726000", // Baldurs Gate: Dark Alliance
"01008c2019598000", // Bluey: The Video Game
"0100c6800b934000", // Brawlhalla
"0100dbf01000a000", // Burnout Paradise Remastered
"0100744001588000", // Cars 3: Driven to Win
@@ -181,7 +228,6 @@ namespace Ryujinx.Common
"01008c8012920000", // Dying Light Platinum Edition
"01001cc01b2d4000", // Goat Simulator 3
"01003620068ea000", // Hand of Fate 2
"0100f7e00c70e000", // Hogwarts Legacy
"010085500130a000", // Lego City: Undercover
"010073c01af34000", // LEGO Horizon Adventures
"0100d71004694000", // Minecraft

View File

@@ -1,6 +1,4 @@
using ARMeilleure.Memory;
using Humanizer;
using Ryujinx.Common.Logging;
using Ryujinx.Memory;
using System;
using System.Collections.Generic;
@@ -17,8 +15,9 @@ namespace Ryujinx.Cpu.LightningJit.Cache
private static readonly int _pageMask = _pageSize - 1;
private const int CodeAlignment = 4; // Bytes.
private const int CacheSize = 256 * 1024 * 1024;
private const int CacheSize = 2047 * 1024 * 1024;
private static ReservedRegion _jitRegion;
private static JitCacheInvalidation _jitCacheInvalidator;
private static CacheMemoryAllocator _cacheAllocator;
@@ -27,8 +26,6 @@ namespace Ryujinx.Cpu.LightningJit.Cache
private static readonly Lock _lock = new();
private static bool _initialized;
private static readonly List<ReservedRegion> _jitRegions = [];
private static int _activeRegionIndex = 0;
[SupportedOSPlatform("windows")]
[LibraryImport("kernel32.dll", SetLastError = true)]
@@ -48,9 +45,7 @@ namespace Ryujinx.Cpu.LightningJit.Cache
return;
}
ReservedRegion firstRegion = new(allocator, CacheSize);
_jitRegions.Add(firstRegion);
_activeRegionIndex = 0;
_jitRegion = new ReservedRegion(allocator, CacheSize);
if (!OperatingSystem.IsWindows() && !OperatingSystem.IsMacOS())
{
@@ -70,8 +65,8 @@ namespace Ryujinx.Cpu.LightningJit.Cache
Debug.Assert(_initialized);
int funcOffset = Allocate(code.Length);
ReservedRegion targetRegion = _jitRegions[_activeRegionIndex];
nint funcPtr = targetRegion.Pointer + funcOffset;
nint funcPtr = _jitRegion.Pointer + funcOffset;
if (OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
{
@@ -85,11 +80,18 @@ namespace Ryujinx.Cpu.LightningJit.Cache
}
else
{
ReprotectAsWritable(targetRegion, funcOffset, code.Length);
Marshal.Copy(code.ToArray(), 0, funcPtr, code.Length);
ReprotectAsExecutable(targetRegion, funcOffset, code.Length);
ReprotectAsWritable(funcOffset, code.Length);
code.CopyTo(new Span<byte>((void*)funcPtr, code.Length));
ReprotectAsExecutable(funcOffset, code.Length);
_jitCacheInvalidator?.Invalidate(funcPtr, (ulong)code.Length);
if (OperatingSystem.IsWindows() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
{
FlushInstructionCache(Process.GetCurrentProcess().Handle, funcPtr, (nuint)code.Length);
}
else
{
_jitCacheInvalidator?.Invalidate(funcPtr, (ulong)code.Length);
}
}
Add(funcOffset, code.Length);
@@ -104,80 +106,50 @@ namespace Ryujinx.Cpu.LightningJit.Cache
{
Debug.Assert(_initialized);
foreach (ReservedRegion region in _jitRegions)
int funcOffset = (int)(pointer.ToInt64() - _jitRegion.Pointer.ToInt64());
if (TryFind(funcOffset, out CacheEntry entry, out int entryIndex) && entry.Offset == funcOffset)
{
if (pointer.ToInt64() < region.Pointer.ToInt64() ||
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;
_cacheAllocator.Free(funcOffset, AlignCodeSize(entry.Size));
_cacheEntries.RemoveAt(entryIndex);
}
}
}
private static void ReprotectAsWritable(ReservedRegion region, int offset, int size)
private static void ReprotectAsWritable(int offset, int size)
{
int endOffs = offset + size;
int regionStart = offset & ~_pageMask;
int regionEnd = (endOffs + _pageMask) & ~_pageMask;
region.Block.MapAsRwx((ulong)regionStart, (ulong)(regionEnd - regionStart));
_jitRegion.Block.MapAsRwx((ulong)regionStart, (ulong)(regionEnd - regionStart));
}
private static void ReprotectAsExecutable(ReservedRegion region, int offset, int size)
private static void ReprotectAsExecutable(int offset, int size)
{
int endOffs = offset + size;
int regionStart = offset & ~_pageMask;
int regionEnd = (endOffs + _pageMask) & ~_pageMask;
region.Block.MapAsRx((ulong)regionStart, (ulong)(regionEnd - regionStart));
_jitRegion.Block.MapAsRx((ulong)regionStart, (ulong)(regionEnd - regionStart));
}
private static int Allocate(int codeSize)
{
codeSize = AlignCodeSize(codeSize);
for (int i = _activeRegionIndex; i < _jitRegions.Count; i++)
int allocOffset = _cacheAllocator.Allocate(codeSize);
if (allocOffset < 0)
{
int allocOffset = _cacheAllocator.Allocate(codeSize);
if (allocOffset >= 0)
{
_jitRegions[i].ExpandIfNeeded((ulong)allocOffset + (ulong)codeSize);
_activeRegionIndex = i;
return allocOffset;
}
throw new OutOfMemoryException("JIT Cache exhausted.");
}
int exhaustedRegion = _activeRegionIndex;
ReservedRegion newRegion = new(_jitRegions[0].Allocator, CacheSize);
_jitRegions.Add(newRegion);
_activeRegionIndex = _jitRegions.Count - 1;
int newRegionNumber = _activeRegionIndex;
_jitRegion.ExpandIfNeeded((ulong)allocOffset + (ulong)codeSize);
Logger.Warning?.Print(LogClass.Cpu, $"JIT Cache Region {exhaustedRegion} exhausted, creating new Cache Region {newRegionNumber} ({((long)(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;
return allocOffset;
}
private static int AlignCodeSize(int codeSize)

View File

@@ -12,7 +12,7 @@ namespace Ryujinx.Cpu.LightningJit.Cache
{
private const int CodeAlignment = 4; // Bytes.
private const int SharedCacheSize = 2047 * 1024 * 1024;
private const int LocalCacheSize = 256 * 1024 * 1024;
private const int LocalCacheSize = 128 * 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
// and allow the guest to take the fast path.

View File

@@ -3,7 +3,6 @@
<PropertyGroup>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
<Configurations>Debug;Release;DebugAOT;ReleaseAOT</Configurations>
</PropertyGroup>
<ItemGroup>

View File

@@ -2,7 +2,6 @@
<PropertyGroup>
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
<Configurations>Debug;Release;DebugAOT;ReleaseAOT</Configurations>
</PropertyGroup>
<ItemGroup>

View File

@@ -0,0 +1,18 @@
namespace Ryujinx.Graphics.GAL
{
public readonly struct ComputeSize
{
public readonly static ComputeSize VtgAsCompute = new(32, 32, 1);
public readonly int X;
public readonly int Y;
public readonly int Z;
public ComputeSize(int x, int y, int z)
{
X = x;
Y = y;
Z = z;
}
}
}

View File

@@ -339,6 +339,84 @@ namespace Ryujinx.Graphics.GAL
return 1;
}
/// <summary>
/// Get bytes per element for this format.
/// </summary>
/// <param name="format">Texture format</param>
/// <returns>Byte size for an element of this format (pixel, vertex attribute, etc)</returns>
public static int GetBytesPerElement(this Format format)
{
int scalarSize = format.GetScalarSize();
switch (format)
{
case Format.R8G8Unorm:
case Format.R8G8Snorm:
case Format.R8G8Uint:
case Format.R8G8Sint:
case Format.R8G8Uscaled:
case Format.R8G8Sscaled:
case Format.R16G16Float:
case Format.R16G16Unorm:
case Format.R16G16Snorm:
case Format.R16G16Uint:
case Format.R16G16Sint:
case Format.R16G16Uscaled:
case Format.R16G16Sscaled:
case Format.R32G32Float:
case Format.R32G32Uint:
case Format.R32G32Sint:
case Format.R32G32Uscaled:
case Format.R32G32Sscaled:
return 2 * scalarSize;
case Format.R8G8B8Unorm:
case Format.R8G8B8Snorm:
case Format.R8G8B8Uint:
case Format.R8G8B8Sint:
case Format.R8G8B8Uscaled:
case Format.R8G8B8Sscaled:
case Format.R16G16B16Float:
case Format.R16G16B16Unorm:
case Format.R16G16B16Snorm:
case Format.R16G16B16Uint:
case Format.R16G16B16Sint:
case Format.R16G16B16Uscaled:
case Format.R16G16B16Sscaled:
case Format.R32G32B32Float:
case Format.R32G32B32Uint:
case Format.R32G32B32Sint:
case Format.R32G32B32Uscaled:
case Format.R32G32B32Sscaled:
return 3 * scalarSize;
case Format.R8G8B8A8Unorm:
case Format.R8G8B8A8Snorm:
case Format.R8G8B8A8Uint:
case Format.R8G8B8A8Sint:
case Format.R8G8B8A8Srgb:
case Format.R8G8B8A8Uscaled:
case Format.R8G8B8A8Sscaled:
case Format.B8G8R8A8Unorm:
case Format.B8G8R8A8Srgb:
case Format.R16G16B16A16Float:
case Format.R16G16B16A16Unorm:
case Format.R16G16B16A16Snorm:
case Format.R16G16B16A16Uint:
case Format.R16G16B16A16Sint:
case Format.R16G16B16A16Uscaled:
case Format.R16G16B16A16Sscaled:
case Format.R32G32B32A32Float:
case Format.R32G32B32A32Uint:
case Format.R32G32B32A32Sint:
case Format.R32G32B32A32Uscaled:
case Format.R32G32B32A32Sscaled:
return 4 * scalarSize;
}
return scalarSize;
}
/// <summary>
/// Checks if the texture format is a depth or depth-stencil format.
/// </summary>

View File

@@ -1,6 +1,4 @@
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging;
using Ryujinx.Graphics.GAL.Multithreading;
using System;
using System.Threading;
@@ -12,20 +10,6 @@ namespace Ryujinx.Graphics.GAL
bool PreferThreading { get; }
public IRenderer TryMakeThreaded(BackendThreading backendThreading = BackendThreading.Auto)
{
if (backendThreading is BackendThreading.On ||
(backendThreading is BackendThreading.Auto && PreferThreading))
{
Logger.Info?.PrintMsg(LogClass.Gpu, $"Backend Threading ({backendThreading}): True");
return new ThreadedRenderer(this);
}
Logger.Info?.PrintMsg(LogClass.Gpu, $"Backend Threading ({backendThreading}): False");
return this;
}
IPipeline Pipeline { get; }
IWindow Window { get; }

View File

@@ -2,25 +2,16 @@
<PropertyGroup>
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
<Configurations>Debug;Release;DebugAOT;ReleaseAOT</Configurations>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseAOT|AnyCPU'">
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugAOT|AnyCPU'">
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Ryujinx.Graphics.Shader\Ryujinx.Graphics.Shader.csproj" />
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />

View File

@@ -4,23 +4,22 @@ namespace Ryujinx.Graphics.GAL
{
public int FragmentOutputMap { get; }
public ResourceLayout ResourceLayout { get; }
public ComputeSize ComputeLocalSize { get; }
public ProgramPipelineState? State { get; }
public bool FromCache { get; set; }
public ShaderInfo(int fragmentOutputMap, ResourceLayout resourceLayout, ProgramPipelineState state, bool fromCache = false)
public ShaderInfo(
int fragmentOutputMap,
ResourceLayout resourceLayout,
ComputeSize computeLocalSize,
ProgramPipelineState? state,
bool fromCache = false)
{
FragmentOutputMap = fragmentOutputMap;
ResourceLayout = resourceLayout;
ComputeLocalSize = computeLocalSize;
State = state;
FromCache = fromCache;
}
public ShaderInfo(int fragmentOutputMap, ResourceLayout resourceLayout, bool fromCache = false)
{
FragmentOutputMap = fragmentOutputMap;
ResourceLayout = resourceLayout;
State = null;
FromCache = fromCache;
}
}
}

View File

@@ -98,7 +98,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
// Make sure all pending uniform buffer data is written to memory.
_3dEngine.FlushUboDirty();
uint qmdAddress = _state.State.SendPcasA;
ComputeQmd qmd = _channel.MemoryManager.Read<ComputeQmd>((ulong)qmdAddress << 8);
@@ -106,6 +106,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
ulong shaderGpuVa = ((ulong)_state.State.SetProgramRegionAAddressUpper << 32) | _state.State.SetProgramRegionB;
shaderGpuVa += (uint)qmd.ProgramOffset;
ShaderCache shaderCache = memoryManager.GetBackingMemory(shaderGpuVa).ShaderCache;
int localMemorySize = qmd.ShaderLocalMemoryLowSize + qmd.ShaderLocalMemoryHighSize;
@@ -142,7 +144,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
sharedMemorySize,
_channel.BufferManager.HasUnalignedStorageBuffers);
CachedShaderProgram cs = memoryManager.Physical.ShaderCache.GetComputeShader(_channel, samplerPoolMaximumId, poolState, computeState, shaderGpuVa);
CachedShaderProgram cs = shaderCache.GetComputeShader(_channel, samplerPoolMaximumId, poolState, computeState, shaderGpuVa);
_context.Renderer.Pipeline.SetProgram(cs.HostProgram);
@@ -156,10 +158,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
{
BufferDescriptor sb = info.SBuffers[index];
ulong sbDescAddress = _channel.BufferManager.GetComputeUniformBufferAddress(sb.SbCbSlot);
(PhysicalMemory physical, ulong sbDescAddress) = _channel.BufferManager.GetComputeUniformBufferAddress(sb.SbCbSlot);
sbDescAddress += (ulong)sb.SbCbOffset * 4;
SbDescriptor sbDescriptor = _channel.MemoryManager.Physical.Read<SbDescriptor>(sbDescAddress);
SbDescriptor sbDescriptor = physical.Read<SbDescriptor>(sbDescAddress);
uint size;
if (sb.SbCbSlot == Constants.DriverReservedUniformBuffer)
@@ -187,7 +189,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
sharedMemorySize,
_channel.BufferManager.HasUnalignedStorageBuffers);
cs = memoryManager.Physical.ShaderCache.GetComputeShader(_channel, samplerPoolMaximumId, poolState, computeState, shaderGpuVa);
cs = shaderCache.GetComputeShader(_channel, samplerPoolMaximumId, poolState, computeState, shaderGpuVa);
_context.Renderer.Pipeline.SetProgram(cs.HostProgram);
}

View File

@@ -215,7 +215,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
_channel.TextureManager.RefreshModifiedTextures();
_3dEngine.CreatePendingSyncs();
_3dEngine.FlushUboDirty();
PhysicalMemory srcPhysical = memoryManager.GetBackingMemory(srcGpuVa);
PhysicalMemory dstPhysical = memoryManager.GetBackingMemory(dstGpuVa);
if (copy2D)
{
// Buffer to texture copy.
@@ -293,7 +296,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
if (completeSource && completeDest && !srcLinear && isIdentityRemap)
{
Image.Texture source = memoryManager.Physical.TextureCache.FindTexture(
Image.Texture source = srcPhysical.TextureCache.FindTexture(
memoryManager,
srcGpuVa,
srcBpp,
@@ -309,7 +312,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
{
source.SynchronizeMemory();
Image.Texture target = memoryManager.Physical.TextureCache.FindOrCreateTexture(
Image.Texture target = dstPhysical.TextureCache.FindOrCreateTexture(
memoryManager,
source.Info.FormatInfo,
dstGpuVa,
@@ -339,7 +342,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
if (completeSource && completeDest && !(dstLinear && !srcLinear) && isIdentityRemap)
{
Image.Texture target = memoryManager.Physical.TextureCache.FindTexture(
Image.Texture target = dstPhysical.TextureCache.FindTexture(
memoryManager,
dstGpuVa,
dstBpp,
@@ -462,6 +465,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
}
else
{
BufferCache bufferCache = dstPhysical.BufferCache;
if (remap &&
_state.State.SetRemapComponentsDstX == SetRemapComponentsDst.ConstA &&
_state.State.SetRemapComponentsDstY == SetRemapComponentsDst.ConstA &&
@@ -472,7 +476,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
_state.State.SetRemapComponentsComponentSize == SetRemapComponentsComponentSize.Four)
{
// Fast path for clears when remap is enabled.
memoryManager.Physical.BufferCache.ClearBuffer(memoryManager, dstGpuVa, size * 4, _state.State.SetRemapConstA);
bufferCache.ClearBuffer(memoryManager, dstGpuVa, size * 4, _state.State.SetRemapConstA);
}
else
{
@@ -492,7 +496,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
}
else
{
memoryManager.Physical.BufferCache.CopyBuffer(memoryManager, srcGpuVa, dstGpuVa, size);
BufferCache.CopyBuffer(_context,memoryManager, srcGpuVa, dstGpuVa, size);
}
}
}

View File

@@ -185,7 +185,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.InlineToMemory
// Right now the copy code at the bottom assumes that it is used on both which might be incorrect.
if (!_isLinear)
{
Image.Texture target = memoryManager.Physical.TextureCache.FindTexture(
Image.Texture target = memoryManager.GetBackingMemory(_dstGpuVa).TextureCache.FindTexture(
memoryManager,
_dstGpuVa,
1,

View File

@@ -1,7 +1,6 @@
using Ryujinx.Graphics.Device;
using Ryujinx.Graphics.Gpu.Engine.GPFifo;
using System;
using System.Runtime.CompilerServices;
namespace Ryujinx.Graphics.Gpu.Engine.MME
{
@@ -53,7 +52,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
{
_executionEngine = new MacroHLE(processor, _hleFunction);
}
else if (GraphicsConfig.EnableMacroJit && RuntimeFeature.IsDynamicCodeSupported)
else if (GraphicsConfig.EnableMacroJit)
{
_executionEngine = new MacroJit();
}

View File

@@ -384,7 +384,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
ulong indirectBufferGpuVa = count.GpuVa;
BufferCache bufferCache = _processor.MemoryManager.Physical.BufferCache;
BufferCache bufferCache = _processor.MemoryManager.GetBackingMemory(indirectBufferGpuVa).BufferCache;
bool useBuffer = bufferCache.CheckModified(_processor.MemoryManager, indirectBufferGpuVa, IndirectIndexedDataEntrySize, out ulong indirectBufferAddress);
@@ -394,6 +394,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
_processor.ThreedClass.DrawIndirect(
topology,
bufferCache,
null,
new MultiRange(indirectBufferAddress, IndirectIndexedDataEntrySize),
default,
1,
@@ -491,22 +493,24 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
}
}
}
BufferCache bufferCache = _processor.MemoryManager.Physical.BufferCache;
BufferCache indirectBufferCache = _processor.MemoryManager.GetBackingMemory(indirectBufferGpuVa).BufferCache;
BufferCache parameterBufferCache = _processor.MemoryManager.GetBackingMemory(parameterBufferGpuVa).BufferCache;
ulong indirectBufferSize = (ulong)maxDrawCount * (ulong)stride;
MultiRange indirectBufferRange = bufferCache.TranslateAndCreateMultiBuffers(_processor.MemoryManager, indirectBufferGpuVa, indirectBufferSize, BufferStage.Indirect);
MultiRange parameterBufferRange = bufferCache.TranslateAndCreateMultiBuffers(_processor.MemoryManager, parameterBufferGpuVa, 4, BufferStage.Indirect);
MultiRange indirectBufferRange = indirectBufferCache.TranslateAndCreateMultiBuffers(_processor.MemoryManager, indirectBufferGpuVa, indirectBufferSize, BufferStage.Indirect);
MultiRange parameterBufferRange = parameterBufferCache.TranslateAndCreateMultiBuffers(_processor.MemoryManager, parameterBufferGpuVa, 4, BufferStage.Indirect);
_processor.ThreedClass.DrawIndirect(
topology,
indirectBufferCache,
parameterBufferCache,
indirectBufferRange,
parameterBufferRange,
maxDrawCount,
stride,
indexCount,
Threed.IndirectDrawType.DrawIndexedIndirectCount);
IndirectDrawType.DrawIndexedIndirectCount);
}
/// <summary>

View File

@@ -200,11 +200,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
int vertexInfoBinding = _vertexAsCompute.Reservations.VertexInfoConstantBufferBinding;
BufferRange vertexInfoRange = new(_vacContext.VertexInfoBufferUpdater.Handle, 0, VertexInfoBuffer.RequiredSize);
_context.Renderer.Pipeline.SetUniformBuffers([new BufferAssignment(vertexInfoBinding, vertexInfoRange)]);
_context.Renderer.Pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(vertexInfoBinding, vertexInfoRange) });
int vertexDataBinding = _vertexAsCompute.Reservations.VertexOutputStorageBufferBinding;
BufferRange vertexDataRange = _vacContext.GetVertexDataBufferRange(_vertexDataOffset, _vertexDataSize, write: true);
_context.Renderer.Pipeline.SetStorageBuffers([new BufferAssignment(vertexDataBinding, vertexDataRange)]);
_context.Renderer.Pipeline.SetStorageBuffers(stackalloc[] { new BufferAssignment(vertexDataBinding, vertexDataRange) });
_vacContext.VertexInfoBufferUpdater.Commit();
@@ -232,7 +232,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
int vertexInfoBinding = _vertexAsCompute.Reservations.VertexInfoConstantBufferBinding;
BufferRange vertexInfoRange = new(_vacContext.VertexInfoBufferUpdater.Handle, 0, VertexInfoBuffer.RequiredSize);
_context.Renderer.Pipeline.SetUniformBuffers([new BufferAssignment(vertexInfoBinding, vertexInfoRange)]);
_context.Renderer.Pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(vertexInfoBinding, vertexInfoRange) });
int vertexDataBinding = _vertexAsCompute.Reservations.VertexOutputStorageBufferBinding;
@@ -250,11 +250,12 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
BufferRange vertexBuffer = _vacContext.GetGeometryVertexDataBufferRange(_geometryVertexDataOffset, _geometryVertexDataSize, write: true);
BufferRange indexBuffer = _vacContext.GetGeometryIndexDataBufferRange(_geometryIndexDataOffset, _geometryIndexDataSize, write: true);
_context.Renderer.Pipeline.SetStorageBuffers([
_context.Renderer.Pipeline.SetStorageBuffers(stackalloc[]
{
new BufferAssignment(vertexDataBinding, vertexDataRange),
new BufferAssignment(geometryVbBinding, vertexBuffer),
new BufferAssignment(geometryIbBinding, indexBuffer)
]);
new BufferAssignment(geometryIbBinding, indexBuffer),
});
_context.Renderer.Pipeline.DispatchCompute(
BitUtils.DivRoundUp(primitivesCount, ComputeLocalSize),
@@ -298,7 +299,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
_context.Renderer.Pipeline.SetProgram(_vertexPassthroughProgram);
_context.Renderer.Pipeline.SetIndexBuffer(indexBuffer, IndexType.UInt);
_context.Renderer.Pipeline.SetStorageBuffers([new BufferAssignment(vertexDataBinding, vertexBuffer)]);
_context.Renderer.Pipeline.SetStorageBuffers(stackalloc[] { new BufferAssignment(vertexDataBinding, vertexBuffer) });
_context.Renderer.Pipeline.SetPrimitiveRestart(true, -1);
_context.Renderer.Pipeline.SetPrimitiveTopology(GetGeometryOutputTopology(_geometryAsCompute.Info.GeometryVerticesPerPrimitive));
@@ -313,7 +314,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
BufferRange vertexDataRange = _vacContext.GetVertexDataBufferRange(_vertexDataOffset, _vertexDataSize, write: false);
_context.Renderer.Pipeline.SetProgram(_vertexPassthroughProgram);
_context.Renderer.Pipeline.SetStorageBuffers([new BufferAssignment(vertexDataBinding, vertexDataRange)]);
_context.Renderer.Pipeline.SetStorageBuffers(stackalloc[] { new BufferAssignment(vertexDataBinding, vertexDataRange) });
_context.Renderer.Pipeline.Draw(_count, _instanceCount, 0, 0);
}
}
@@ -370,7 +371,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
{
MemoryManager memoryManager = _channel.MemoryManager;
BufferRange range = memoryManager.Physical.BufferCache.GetBufferRange(memoryManager.GetPhysicalRegions(address, size), BufferStage.VertexBuffer);
BufferRange range = memoryManager.GetBackingMemory(address).BufferCache.GetBufferRange(memoryManager.GetPhysicalRegions(address, size), BufferStage.VertexBuffer);
ITexture bufferTexture = _vacContext.EnsureBufferTexture(index + 2, format);
bufferTexture.SetStorage(range);
@@ -412,7 +413,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
MemoryManager memoryManager = _channel.MemoryManager;
ulong misalign = address & ((ulong)_context.Capabilities.TextureBufferOffsetAlignment - 1);
BufferRange range = memoryManager.Physical.BufferCache.GetBufferRange(
BufferRange range = memoryManager.GetBackingMemory(address).BufferCache.GetBufferRange(
memoryManager.GetPhysicalRegions(address + indexOffset - misalign, size + misalign),
BufferStage.IndexBuffer);
misalignedOffset = (int)misalign >> shift;

View File

@@ -16,6 +16,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
// State associated with direct uniform buffer updates.
// This state is used to attempt to batch together consecutive updates.
private ulong _ubBeginGpuAddress = 0;
private ulong _ubBeginCpuAddress = 0;
private ulong _ubFollowUpAddress = 0;
private ulong _ubByteCount = 0;
@@ -113,12 +114,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
if (_ubFollowUpAddress != 0)
{
MemoryManager memoryManager = _channel.MemoryManager;
PhysicalMemory physicalMemory = memoryManager.GetBackingMemory(_ubBeginGpuAddress);
Span<byte> data = MemoryMarshal.Cast<int, byte>(_ubData.AsSpan(0, (int)(_ubByteCount / 4)));
if (memoryManager.Physical.WriteWithRedundancyCheck(_ubBeginCpuAddress, data))
if (physicalMemory.WriteWithRedundancyCheck(_ubBeginCpuAddress, data))
{
memoryManager.Physical.BufferCache.ForceDirty(memoryManager, _ubFollowUpAddress - _ubByteCount, _ubByteCount);
physicalMemory.BufferCache.ForceDirty(memoryManager, _ubFollowUpAddress - _ubByteCount, _ubByteCount);
}
_ubFollowUpAddress = 0;

View File

@@ -641,6 +641,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
public void DrawIndirect(
ThreedClass engine,
PrimitiveTopology topology,
BufferCache indirectBufferCache,
BufferCache parameterBufferCache,
MultiRange indirectBufferRange,
MultiRange parameterBufferRange,
int maxDrawCount,
@@ -662,8 +664,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
return;
}
PhysicalMemory memory = _channel.MemoryManager.Physical;
bool hasCount = (drawType & IndirectDrawType.Count) != 0;
bool indexed = (drawType & IndirectDrawType.Indexed) != 0;
@@ -684,8 +684,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
if (hasCount)
{
BufferRange indirectBuffer = memory.BufferCache.GetBufferRange(indirectBufferRange, BufferStage.Indirect);
BufferRange parameterBuffer = memory.BufferCache.GetBufferRange(parameterBufferRange, BufferStage.Indirect);
BufferRange indirectBuffer = indirectBufferCache.GetBufferRange(indirectBufferRange, BufferStage.Indirect);
BufferRange parameterBuffer = parameterBufferCache.GetBufferRange(parameterBufferRange, BufferStage.Indirect);
if (indexed)
{
@@ -698,7 +698,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
}
else
{
BufferRange indirectBuffer = memory.BufferCache.GetBufferRange(indirectBufferRange, BufferStage.Indirect);
BufferRange indirectBuffer = indirectBufferCache.GetBufferRange(indirectBufferRange, BufferStage.Indirect);
if (indexed)
{
@@ -913,7 +913,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
Span<Rectangle<int>> scissors =
[
new(scissorX, scissorY, scissorW, scissorH)
new Rectangle<int>(scissorX, scissorY, scissorW, scissorH)
];
_context.Renderer.Pipeline.SetScissors(scissors);

View File

@@ -381,10 +381,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
{
BufferDescriptor sb = info.SBuffers[index];
ulong sbDescAddress = _channel.BufferManager.GetGraphicsUniformBufferAddress(stage, sb.SbCbSlot);
(PhysicalMemory physical, ulong sbDescAddress) = _channel.BufferManager.GetGraphicsUniformBufferAddress(stage, sb.SbCbSlot);
sbDescAddress += (ulong)sb.SbCbOffset * 4;
SbDescriptor sbDescriptor = _channel.MemoryManager.Physical.Read<SbDescriptor>(sbDescAddress);
SbDescriptor sbDescriptor = physical.Read<SbDescriptor>(sbDescAddress);
uint size;
if (sb.SbCbSlot == Constants.DriverReservedUniformBuffer)
@@ -505,7 +505,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
rtNoAlphaMask |= 1u << index;
}
Image.Texture color = memoryManager.Physical.TextureCache.FindOrCreateTexture(
TextureCache colorTextureCache = memoryManager.GetBackingMemory(colorState.Address.Pack()).TextureCache;
Image.Texture color = colorTextureCache.FindOrCreateTexture(
memoryManager,
colorState,
_vtgWritesRtLayer || layered,
@@ -513,7 +515,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
samplesInX,
samplesInY,
sizeHint);
changedScale |= _channel.TextureManager.SetRenderTargetColor(index, color);
if (color != null)
@@ -543,8 +545,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
{
RtDepthStencilState dsState = _state.State.RtDepthStencilState;
Size3D dsSize = _state.State.RtDepthStencilSize;
depthStencil = memoryManager.Physical.TextureCache.FindOrCreateTexture(
TextureCache dsTextureCache = memoryManager.GetBackingMemory(dsState.Address.Pack()).TextureCache;
depthStencil = dsTextureCache.FindOrCreateTexture(
memoryManager,
dsState,
dsSize,
@@ -1409,8 +1412,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// </summary>
private void UpdateShaderState()
{
ShaderCache shaderCache = _channel.MemoryManager.Physical.ShaderCache;
_vtgWritesRtLayer = false;
ShaderAddresses addresses = new();
@@ -1433,6 +1434,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
? _state.State.TexturePoolState.MaximumId
: _state.State.SamplerPoolState.MaximumId;
// Shader stages on different address spaces are not supported right now,
// but it should never happen in practice anyway.
ShaderCache shaderCache = _channel.MemoryManager.GetBackingMemory(addresses.VertexB).ShaderCache;
CachedShaderProgram gs = shaderCache.GetGraphicsShader(
ref _state.State,
ref _pipeline,

View File

@@ -5,6 +5,7 @@ using Ryujinx.Graphics.Gpu.Engine.GPFifo;
using Ryujinx.Graphics.Gpu.Engine.InlineToMemory;
using Ryujinx.Graphics.Gpu.Engine.Threed.Blender;
using Ryujinx.Graphics.Gpu.Engine.Types;
using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Gpu.Synchronization;
using Ryujinx.Memory.Range;
using System;
@@ -804,6 +805,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// Performs a indirect draw, with parameters from a GPU buffer.
/// </summary>
/// <param name="topology">Primitive topology</param>
/// <param name="indirectBufferCache">Buffer cache owning the buffer with the draw parameters</param>
/// <param name="parameterBufferCache">Buffer cache owning the buffer with the draw count</param>
/// <param name="indirectBufferRange">Memory range of the buffer with the draw parameters, such as count, first index, etc</param>
/// <param name="parameterBufferRange">Memory range of the buffer with the draw count</param>
/// <param name="maxDrawCount">Maximum number of draws that can be made</param>
@@ -812,6 +815,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// <param name="drawType">Type of the indirect draw, which can be indexed or non-indexed, with or without a draw count</param>
public void DrawIndirect(
PrimitiveTopology topology,
BufferCache indirectBufferCache,
BufferCache parameterBufferCache,
MultiRange indirectBufferRange,
MultiRange parameterBufferRange,
int maxDrawCount,
@@ -819,7 +824,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
int indexCount,
IndirectDrawType drawType)
{
_drawManager.DrawIndirect(this, topology, indirectBufferRange, parameterBufferRange, maxDrawCount, stride, indexCount, drawType);
_drawManager.DrawIndirect(this, topology, indirectBufferCache, parameterBufferCache, indirectBufferRange, parameterBufferRange, maxDrawCount, stride, indexCount, drawType);
}
/// <summary>

View File

@@ -233,6 +233,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod
TwodTexture dstCopyTexture = Unsafe.As<uint, TwodTexture>(ref _state.State.SetDstFormat);
TwodTexture srcCopyTexture = Unsafe.As<uint, TwodTexture>(ref _state.State.SetSrcFormat);
TextureCache srcTextureCache = memoryManager.GetBackingMemory(srcCopyTexture.Address.Pack()).TextureCache;
TextureCache dstTextureCache = memoryManager.GetBackingMemory(dstCopyTexture.Address.Pack()).TextureCache;
long srcX = ((long)_state.State.SetPixelsFromMemorySrcX0Int << 32) | (long)(ulong)_state.State.SetPixelsFromMemorySrcX0Frac;
long srcY = ((long)_state.State.PixelsFromMemorySrcY0Int << 32) | (long)(ulong)_state.State.SetPixelsFromMemorySrcY0Frac;
@@ -305,7 +308,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod
// are the same, as we can't blit between different depth formats.
bool srcDepthAlias = srcCopyTexture.Format == dstCopyTexture.Format;
Image.Texture srcTexture = memoryManager.Physical.TextureCache.FindOrCreateTexture(
Image.Texture srcTexture = srcTextureCache.FindOrCreateTexture(
memoryManager,
srcCopyTexture,
offset,
@@ -326,7 +329,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod
return;
}
memoryManager.Physical.TextureCache.Lift(srcTexture);
srcTextureCache.Lift(srcTexture);
// When the source texture that was found has a depth format,
// we must enforce the target texture also has a depth format,
@@ -342,7 +345,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod
dstCopyTextureFormat = dstCopyTexture.Format.Convert();
}
Image.Texture dstTexture = memoryManager.Physical.TextureCache.FindOrCreateTexture(
Image.Texture dstTexture = dstTextureCache.FindOrCreateTexture(
memoryManager,
dstCopyTexture,
0,

View File

@@ -58,22 +58,24 @@ namespace Ryujinx.Graphics.Gpu
public void BindMemory(MemoryManager memoryManager)
{
MemoryManager oldMemoryManager = Interlocked.Exchange(ref _memoryManager, memoryManager ?? throw new ArgumentNullException(nameof(memoryManager)));
if (oldMemoryManager == memoryManager)
{
return;
}
memoryManager.Physical.IncrementReferenceCount();
memoryManager.AttachToChannel(BufferManager.Rebind);
if (oldMemoryManager != null)
{
oldMemoryManager.Physical.BufferCache.NotifyBuffersModified -= BufferManager.Rebind;
oldMemoryManager.Physical.DecrementReferenceCount();
oldMemoryManager.DetachFromChannel(BufferManager.Rebind);
oldMemoryManager.MemoryUnmapped -= MemoryUnmappedHandler;
}
memoryManager.Physical.BufferCache.NotifyBuffersModified += BufferManager.Rebind;
memoryManager.MemoryUnmapped += MemoryUnmappedHandler;
// Since the memory manager changed, make sure we will get pools from addresses of the new memory manager.
TextureManager.ReloadPools();
memoryManager.Physical.BufferCache.QueuePrune();
memoryManager.QueuePrune();
}
/// <summary>
@@ -86,7 +88,7 @@ namespace Ryujinx.Graphics.Gpu
TextureManager.ReloadPools();
MemoryManager memoryManager = Volatile.Read(ref _memoryManager);
memoryManager?.Physical.BufferCache.QueuePrune();
memoryManager?.QueuePrune();
}
/// <summary>
@@ -141,8 +143,7 @@ namespace Ryujinx.Graphics.Gpu
MemoryManager oldMemoryManager = Interlocked.Exchange(ref _memoryManager, null);
if (oldMemoryManager != null)
{
oldMemoryManager.Physical.BufferCache.NotifyBuffersModified -= BufferManager.Rebind;
oldMemoryManager.Physical.DecrementReferenceCount();
oldMemoryManager.DetachFromChannel(BufferManager.Rebind);
oldMemoryManager.MemoryUnmapped -= MemoryUnmappedHandler;
}
}

View File

@@ -6,6 +6,7 @@ using Ryujinx.Graphics.Gpu.Engine.GPFifo;
using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Gpu.Shader;
using Ryujinx.Graphics.Gpu.Synchronization;
using Ryujinx.Memory;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
@@ -172,7 +173,7 @@ namespace Ryujinx.Graphics.Gpu
throw new ArgumentException("The PID is invalid or the process was not registered", nameof(pid));
}
return new MemoryManager(physicalMemory, cpuMemorySize);
return new MemoryManager(this, physicalMemory, cpuMemorySize);
}
/// <summary>
@@ -197,7 +198,7 @@ namespace Ryujinx.Graphics.Gpu
/// <param name="pid">ID of the process that owns <paramref name="cpuMemory"/></param>
/// <param name="cpuMemory">Virtual memory owned by the process</param>
/// <exception cref="ArgumentException">Thrown if <paramref name="pid"/> was already registered</exception>
public void RegisterProcess(ulong pid, Cpu.IVirtualMemoryManagerTracked cpuMemory)
public void RegisterProcess(ulong pid, IVirtualMemoryManagerTracked cpuMemory)
{
PhysicalMemory physicalMemory = new(this, cpuMemory);
if (!PhysicalMemoryRegistry.TryAdd(pid, physicalMemory))

View File

@@ -1,3 +1,4 @@
using Ryujinx.Graphics.Gpu.Memory;
using System;
using System.Collections.Generic;
@@ -64,7 +65,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="maximumId">Maximum ID of the texture pool</param>
/// <param name="bindingsArrayCache">Cache of texture array bindings</param>
/// <returns>The found or newly created texture pool</returns>
public T FindOrCreate(GpuChannel channel, ulong address, int maximumId, TextureBindingsArrayCache bindingsArrayCache)
public T FindOrCreate(GpuChannel channel, PhysicalMemory physicalMemory, ulong address, int maximumId, TextureBindingsArrayCache bindingsArrayCache)
{
// Remove old entries from the cache, if possible.
while (_pools.Count > MaxCapacity && (_currentTimestamp - _pools.First.Value.CacheTimestamp) >= MinDeltaForRemoval)
@@ -99,7 +100,7 @@ namespace Ryujinx.Graphics.Gpu.Image
}
// If not found, create a new one.
pool = CreatePool(_context, channel, address, maximumId);
pool = CreatePool(_context, channel, physicalMemory, address, maximumId);
pool.CacheNode = _pools.AddLast(pool);
pool.CacheTimestamp = _currentTimestamp;
@@ -112,9 +113,10 @@ namespace Ryujinx.Graphics.Gpu.Image
/// </summary>
/// <param name="context">GPU context that the pool belongs to</param>
/// <param name="channel">GPU channel that the pool belongs to</param>
/// <param name="physicalMemory">GPU backing memory of the pool</param>
/// <param name="address">Address of the pool in guest memory</param>
/// <param name="maximumId">Maximum ID of the pool (equal to maximum minus one)</param>
protected abstract T CreatePool(GpuContext context, GpuChannel channel, ulong address, int maximumId);
protected abstract T CreatePool(GpuContext context, GpuChannel channel, PhysicalMemory physicalMemory, ulong address, int maximumId);
public void Dispose()
{

View File

@@ -1,3 +1,5 @@
using Ryujinx.Graphics.Gpu.Memory;
namespace Ryujinx.Graphics.Gpu.Image
{
/// <summary>
@@ -20,11 +22,12 @@ namespace Ryujinx.Graphics.Gpu.Image
/// </summary>
/// <param name="context">GPU context that the sampler pool belongs to</param>
/// <param name="channel">GPU channel that the texture pool belongs to</param>
/// <param name="physicalMemory">GPU backing memory of the pool</param>
/// <param name="address">Address of the sampler pool in guest memory</param>
/// <param name="maximumId">Maximum sampler ID of the sampler pool (equal to maximum samplers minus one)</param>
protected override SamplerPool CreatePool(GpuContext context, GpuChannel channel, ulong address, int maximumId)
protected override SamplerPool CreatePool(GpuContext context, GpuChannel channel, PhysicalMemory physicalMemory, ulong address, int maximumId)
{
return new SamplerPool(context, channel.MemoryManager.Physical, address, maximumId);
return new SamplerPool(context, physicalMemory, address, maximumId);
}
}
}

View File

@@ -660,6 +660,7 @@ namespace Ryujinx.Graphics.Gpu.Image
ISampler[] samplers = isImage ? null : new ISampler[bindingInfo.ArrayLength];
ITexture[] textures = new ITexture[bindingInfo.ArrayLength];
BufferCache bufferCache = null;
for (int index = 0; index < length; index++)
{
@@ -673,7 +674,7 @@ namespace Ryujinx.Graphics.Gpu.Image
else
{
ref readonly TextureDescriptor descriptor = ref texturePool.GetForBinding(index, bindingInfo.FormatInfo, out texture);
bufferCache = _channel.MemoryManager.GetBackingMemory(descriptor.UnpackAddress()).BufferCache;
if (texture != null)
{
entry.Textures[texture] = texture.InvalidatedSequence;
@@ -702,11 +703,10 @@ namespace Ryujinx.Graphics.Gpu.Image
// to ensure we're not using a old buffer that was already deleted.
if (isImage)
{
_channel.BufferManager.SetBufferTextureStorage(stage, entry.ImageArray, hostTexture, texture.Range, bindingInfo, index);
}
_channel.BufferManager.SetBufferTextureStorage(stage, entry.ImageArray, hostTexture, bufferCache, texture.Range, bindingInfo, index); }
else
{
_channel.BufferManager.SetBufferTextureStorage(stage, entry.TextureArray, hostTexture, texture.Range, bindingInfo, index);
_channel.BufferManager.SetBufferTextureStorage(stage, entry.TextureArray, hostTexture, bufferCache, texture.Range, bindingInfo, index);
}
}
else if (isImage)
@@ -797,11 +797,11 @@ namespace Ryujinx.Graphics.Gpu.Image
return;
}
cachedTextureBuffer = MemoryMarshal.Cast<byte, int>(_channel.MemoryManager.Physical.GetSpan(textureBufferBounds.Range));
cachedTextureBuffer = MemoryMarshal.Cast<byte, int>(textureBufferBounds.Physical.GetSpan(textureBufferBounds.Range));
if (separateSamplerBuffer)
{
cachedSamplerBuffer = MemoryMarshal.Cast<byte, int>(_channel.MemoryManager.Physical.GetSpan(samplerBufferBounds.Range));
cachedSamplerBuffer = MemoryMarshal.Cast<byte, int>(samplerBufferBounds.Physical.GetSpan(samplerBufferBounds.Range));
}
else
{
@@ -828,11 +828,10 @@ namespace Ryujinx.Graphics.Gpu.Image
}
else
{
cachedTextureBuffer = MemoryMarshal.Cast<byte, int>(_channel.MemoryManager.Physical.GetSpan(textureBufferBounds.Range));
cachedTextureBuffer = MemoryMarshal.Cast<byte, int>(textureBufferBounds.Physical.GetSpan(textureBufferBounds.Range));
if (separateSamplerBuffer)
{
cachedSamplerBuffer = MemoryMarshal.Cast<byte, int>(_channel.MemoryManager.Physical.GetSpan(samplerBufferBounds.Range));
cachedSamplerBuffer = MemoryMarshal.Cast<byte, int>(samplerBufferBounds.Physical.GetSpan(samplerBufferBounds.Range));
}
else
{
@@ -901,16 +900,18 @@ namespace Ryujinx.Graphics.Gpu.Image
if (hostTexture != null && texture.Target == Target.TextureBuffer)
{
BufferCache bufferCache = textureBufferBounds.BufferCache;
// Ensure that the buffer texture is using the correct buffer as storage.
// Buffers are frequently re-created to accommodate larger data, so we need to re-bind
// to ensure we're not using a old buffer that was already deleted.
if (isImage)
{
_channel.BufferManager.SetBufferTextureStorage(stage, entry.ImageArray, hostTexture, texture.Range, bindingInfo, index);
_channel.BufferManager.SetBufferTextureStorage(stage, entry.ImageArray, hostTexture, bufferCache, texture.Range, bindingInfo, index);
}
else
{
_channel.BufferManager.SetBufferTextureStorage(stage, entry.TextureArray, hostTexture, texture.Range, bindingInfo, index);
_channel.BufferManager.SetBufferTextureStorage(stage, entry.TextureArray, hostTexture, bufferCache, texture.Range, bindingInfo, index);
}
}
else if (isImage)

View File

@@ -396,7 +396,7 @@ namespace Ryujinx.Graphics.Gpu.Image
{
ref BufferBounds bounds = ref _channel.BufferManager.GetUniformBufferBounds(_isCompute, stageIndex, textureBufferIndex);
cachedTextureBuffer = MemoryMarshal.Cast<byte, int>(_channel.MemoryManager.Physical.GetSpan(bounds.Range));
cachedTextureBuffer = MemoryMarshal.Cast<byte, int>(bounds.Physical.GetSpan(bounds.Range));
cachedTextureBufferIndex = textureBufferIndex;
if (samplerBufferIndex == textureBufferIndex)
@@ -410,7 +410,7 @@ namespace Ryujinx.Graphics.Gpu.Image
{
ref BufferBounds bounds = ref _channel.BufferManager.GetUniformBufferBounds(_isCompute, stageIndex, samplerBufferIndex);
cachedSamplerBuffer = MemoryMarshal.Cast<byte, int>(_channel.MemoryManager.Physical.GetSpan(bounds.Range));
cachedSamplerBuffer = MemoryMarshal.Cast<byte, int>(bounds.Physical.GetSpan(bounds.Range));
cachedSamplerBufferIndex = samplerBufferIndex;
}
}
@@ -524,7 +524,8 @@ namespace Ryujinx.Graphics.Gpu.Image
// Ensure that the buffer texture is using the correct buffer as storage.
// Buffers are frequently re-created to accommodate larger data, so we need to re-bind
// to ensure we're not using a old buffer that was already deleted.
_channel.BufferManager.SetBufferTextureStorage(stage, hostTexture, texture.Range, bindingInfo, false);
BufferCache bufferCache = _channel.MemoryManager.GetBackingMemory(descriptor.UnpackAddress()).BufferCache;
_channel.BufferManager.SetBufferTextureStorage(stage, hostTexture, bufferCache, texture.Range, bindingInfo, false);
// Cache is not used for buffer texture, it must always rebind.
state.CachedTexture = null;
@@ -659,7 +660,8 @@ namespace Ryujinx.Graphics.Gpu.Image
// Buffers are frequently re-created to accommodate larger data, so we need to re-bind
// to ensure we're not using a old buffer that was already deleted.
_channel.BufferManager.SetBufferTextureStorage(stage, hostTexture, texture.Range, bindingInfo, true);
BufferCache bufferCache = _channel.MemoryManager.GetBackingMemory(descriptor.UnpackAddress()).BufferCache;
_channel.BufferManager.SetBufferTextureStorage(stage, hostTexture, bufferCache, texture.Range, bindingInfo, true);
// Cache is not used for buffer texture, it must always rebind.
state.CachedTexture = null;
@@ -715,9 +717,10 @@ namespace Ryujinx.Graphics.Gpu.Image
int packedId = ReadPackedId(stageIndex, handle, textureBufferIndex, samplerBufferIndex);
int textureId = TextureHandle.UnpackTextureId(packedId);
PhysicalMemory physical = _channel.MemoryManager.GetBackingMemory(poolGpuVa);
ulong poolAddress = _channel.MemoryManager.Translate(poolGpuVa);
TexturePool texturePool = _texturePoolCache.FindOrCreate(_channel, poolAddress, maximumId, _bindingsArrayCache);
TexturePool texturePool = _texturePoolCache.FindOrCreate(_channel, physical, poolAddress, maximumId, _bindingsArrayCache);
TextureDescriptor descriptor;
@@ -751,12 +754,12 @@ namespace Ryujinx.Graphics.Gpu.Image
{
(int textureWordOffset, int samplerWordOffset, TextureHandleType handleType) = TextureHandle.UnpackOffsets(wordOffset);
ulong textureBufferAddress = _isCompute
(PhysicalMemory texturePhysicalMemory, ulong textureBufferAddress) = _isCompute
? _channel.BufferManager.GetComputeUniformBufferAddress(textureBufferIndex)
: _channel.BufferManager.GetGraphicsUniformBufferAddress(stageIndex, textureBufferIndex);
int handle = textureBufferAddress != MemoryManager.PteUnmapped
? _channel.MemoryManager.Physical.Read<int>(textureBufferAddress + (uint)textureWordOffset * 4)
? texturePhysicalMemory.Read<int>(textureBufferAddress + (uint)textureWordOffset * 4)
: 0;
// The "wordOffset" (which is really the immediate value used on texture instructions on the shader)
@@ -771,12 +774,12 @@ namespace Ryujinx.Graphics.Gpu.Image
if (handleType != TextureHandleType.SeparateConstantSamplerHandle)
{
ulong samplerBufferAddress = _isCompute
(PhysicalMemory samplerPhysicalMemory, ulong samplerBufferAddress) = _isCompute
? _channel.BufferManager.GetComputeUniformBufferAddress(samplerBufferIndex)
: _channel.BufferManager.GetGraphicsUniformBufferAddress(stageIndex, samplerBufferIndex);
samplerHandle = samplerBufferAddress != MemoryManager.PteUnmapped
? _channel.MemoryManager.Physical.Read<int>(samplerBufferAddress + (uint)samplerWordOffset * 4)
? samplerPhysicalMemory.Read<int>(samplerBufferAddress + (uint)samplerWordOffset * 4)
: 0;
}
else
@@ -813,7 +816,8 @@ namespace Ryujinx.Graphics.Gpu.Image
if (poolAddress != MemoryManager.PteUnmapped)
{
texturePool = _texturePoolCache.FindOrCreate(_channel, poolAddress, _texturePoolMaximumId, _bindingsArrayCache);
PhysicalMemory physical = _channel.MemoryManager.GetBackingMemory(_texturePoolGpuVa);
texturePool = _texturePoolCache.FindOrCreate(_channel, physical, poolAddress, _texturePoolMaximumId, _bindingsArrayCache);
_texturePool = texturePool;
}
}
@@ -824,7 +828,8 @@ namespace Ryujinx.Graphics.Gpu.Image
if (poolAddress != MemoryManager.PteUnmapped)
{
samplerPool = _samplerPoolCache.FindOrCreate(_channel, poolAddress, _samplerPoolMaximumId, _bindingsArrayCache);
PhysicalMemory physical = _channel.MemoryManager.GetBackingMemory(_samplerPoolGpuVa);
samplerPool = _samplerPoolCache.FindOrCreate(_channel, physical, poolAddress, _samplerPoolMaximumId, _bindingsArrayCache);
_samplerPool = samplerPool;
}
}

View File

@@ -1,5 +1,6 @@
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Engine.Types;
using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Gpu.Shader;
using System;
@@ -385,8 +386,9 @@ namespace Ryujinx.Graphics.Gpu.Image
public TexturePool GetTexturePool(ulong poolGpuVa, int maximumId)
{
ulong poolAddress = _channel.MemoryManager.Translate(poolGpuVa);
PhysicalMemory physical = _channel.MemoryManager.GetBackingMemory(poolAddress);
TexturePool texturePool = _texturePoolCache.FindOrCreate(_channel, poolAddress, maximumId, _bindingsArrayCache);
TexturePool texturePool = _texturePoolCache.FindOrCreate(_channel, physical, poolAddress, maximumId, _bindingsArrayCache);
return texturePool;
}

View File

@@ -160,9 +160,10 @@ namespace Ryujinx.Graphics.Gpu.Image
/// </summary>
/// <param name="context">GPU context that the texture pool belongs to</param>
/// <param name="channel">GPU channel that the texture pool belongs to</param>
/// <param name="physicalMemory">Backing memory of the pool</param>
/// <param name="address">Address of the texture pool in guest memory</param>
/// <param name="maximumId">Maximum texture ID of the texture pool (equal to maximum textures minus one)</param>
public TexturePool(GpuContext context, GpuChannel channel, ulong address, int maximumId) : base(context, channel.MemoryManager.Physical, address, maximumId)
public TexturePool(GpuContext context, GpuChannel channel, PhysicalMemory physicalMemory, ulong address, int maximumId) : base(context, physicalMemory, address, maximumId)
{
_channel = channel;
_aliasLists = new Dictionary<Texture, TextureAliasList>();
@@ -193,7 +194,9 @@ namespace Ryujinx.Graphics.Gpu.Image
}
TextureInfo info = GetInfo(descriptor, out int layerSize);
texture = PhysicalMemory.TextureCache.FindOrCreateTexture(_channel.MemoryManager, TextureSearchFlags.ForSampler, info, layerSize);
MemoryManager memoryManager = _channel.MemoryManager;
TextureCache textureCache = memoryManager.GetBackingMemory(descriptor.UnpackAddress()).TextureCache;
texture = textureCache.FindOrCreateTexture(memoryManager, TextureSearchFlags.ForSampler, info, layerSize);
// If this happens, then the texture address is invalid, we can't add it to the cache.
if (texture == null)
@@ -421,7 +424,8 @@ namespace Ryujinx.Graphics.Gpu.Image
continue;
}
MultiRange range = _channel.MemoryManager.Physical.TextureCache.UpdatePartiallyMapped(_channel.MemoryManager, address, texture);
TextureCache textureCache = _channel.MemoryManager.GetBackingMemory(address).TextureCache;
MultiRange range = textureCache.UpdatePartiallyMapped(_channel.MemoryManager, address, texture);
// If the texture is not mapped at all, delete its reference.
@@ -446,7 +450,7 @@ namespace Ryujinx.Graphics.Gpu.Image
if (!range.Equals(texture.Range))
{
// Part of the texture was mapped or unmapped. Replace the range and regenerate tracking handles.
if (!_channel.MemoryManager.Physical.TextureCache.UpdateMapping(texture, range))
if (!textureCache.UpdateMapping(texture, range))
{
// Texture could not be remapped due to a collision, just delete it.
if (Interlocked.Exchange(ref Items[request.ID], null) != null)
@@ -481,6 +485,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="size">Size of the range being invalidated</param>
protected override void InvalidateRangeImpl(ulong address, ulong size)
{
MemoryManager memoryManager = _channel.MemoryManager;
ProcessDereferenceQueue();
ulong endAddress = address + size;
@@ -505,7 +510,8 @@ namespace Ryujinx.Graphics.Gpu.Image
if (texture.HasOneReference())
{
_channel.MemoryManager.Physical.TextureCache.AddShortCache(texture, ref cachedDescriptor);
TextureCache textureCache = memoryManager.GetBackingMemory(descriptor.UnpackAddress()).TextureCache;
textureCache.AddShortCache(texture, ref cachedDescriptor);
}
if (Interlocked.Exchange(ref Items[id], null) != null)

View File

@@ -1,3 +1,5 @@
using Ryujinx.Graphics.Gpu.Memory;
namespace Ryujinx.Graphics.Gpu.Image
{
/// <summary>
@@ -20,11 +22,17 @@ namespace Ryujinx.Graphics.Gpu.Image
/// </summary>
/// <param name="context">GPU context that the texture pool belongs to</param>
/// <param name="channel">GPU channel that the texture pool belongs to</param>
/// <param name="physicalMemory">Backing memory of the pool</param>
/// <param name="address">Address of the texture pool in guest memory</param>
/// <param name="maximumId">Maximum texture ID of the texture pool (equal to maximum textures minus one)</param>
protected override TexturePool CreatePool(GpuContext context, GpuChannel channel, ulong address, int maximumId)
protected override TexturePool CreatePool(
GpuContext context,
GpuChannel channel,
PhysicalMemory physicalMemory,
ulong address,
int maximumId)
{
return new TexturePool(context, channel, address, maximumId);
return new TexturePool(context, channel, physicalMemory, address, maximumId);
}
}
}

View File

@@ -9,6 +9,16 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary>
readonly struct BufferBounds : IEquatable<BufferBounds>
{
/// <summary>
/// Physical memory backing the buffer.
/// </summary>
public PhysicalMemory Physical { get; }
/// <summary>
/// Buffer cache that owns the buffer.
/// </summary>
public BufferCache BufferCache => Physical.BufferCache;
/// <summary>
/// Physical memory ranges where the buffer is mapped.
/// </summary>
@@ -29,8 +39,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary>
/// <param name="range">Physical memory ranges where the buffer is mapped</param>
/// <param name="flags">Buffer usage flags</param>
public BufferBounds(MultiRange range, BufferUsageFlags flags = BufferUsageFlags.None)
public BufferBounds(PhysicalMemory physical, MultiRange range, BufferUsageFlags flags = BufferUsageFlags.None)
{
Physical = physical;
Range = range;
Flags = flags;
}

View File

@@ -735,18 +735,22 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <remarks>
/// This does a GPU side copy.
/// </remarks>
/// <param name="context">GPU context</param>
/// <param name="memoryManager">GPU memory manager where the buffer is mapped</param>
/// <param name="srcVa">GPU virtual address of the copy source</param>
/// <param name="dstVa">GPU virtual address of the copy destination</param>
/// <param name="size">Size in bytes of the copy</param>
public void CopyBuffer(MemoryManager memoryManager, ulong srcVa, ulong dstVa, ulong size)
public static void CopyBuffer(GpuContext context, MemoryManager memoryManager, ulong srcVa, ulong dstVa, ulong size)
{
MultiRange srcRange = TranslateAndCreateMultiBuffersPhysicalOnly(memoryManager, srcVa, size, BufferStage.Copy);
MultiRange dstRange = TranslateAndCreateMultiBuffersPhysicalOnly(memoryManager, dstVa, size, BufferStage.Copy);
PhysicalMemory srcPhysical = memoryManager.GetBackingMemory(srcVa);
PhysicalMemory dstPhysical = memoryManager.GetBackingMemory(dstVa);
MultiRange srcRange = srcPhysical.BufferCache.TranslateAndCreateBuffer(memoryManager, srcVa, size, BufferStage.Copy);
MultiRange dstRange = dstPhysical.BufferCache.TranslateAndCreateBuffer(memoryManager, dstVa, size, BufferStage.Copy);
if (srcRange.Count == 1 && dstRange.Count == 1)
{
CopyBufferSingleRange(memoryManager, srcRange.GetSubRange(0).Address, dstRange.GetSubRange(0).Address, size);
CopyBufferSingleRange(context, srcPhysical, dstPhysical, srcRange.GetSubRange(0).Address, dstRange.GetSubRange(0).Address, size);
}
else
{
@@ -777,7 +781,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
ulong dstSize = dstSubRange.Size - dstOffset;
ulong copySize = Math.Min(srcSize, dstSize);
CopyBufferSingleRange(memoryManager, srcSubRange.Address + srcOffset, dstSubRange.Address + dstOffset, copySize);
CopyBufferSingleRange(context, srcPhysical, dstPhysical, srcSubRange.Address + srcOffset, dstSubRange.Address + dstOffset, copySize);
srcOffset += copySize;
dstOffset += copySize;
@@ -793,18 +797,26 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// This does a GPU side copy.
/// </remarks>
/// <param name="memoryManager">GPU memory manager where the buffer is mapped</param>
/// <param name="srcPhysical">Physical memory backing the source buffer.</param>
/// <param name="dstPhysical">Physical memory backing the destination buffer.</param>
/// <param name="srcAddress">Physical address of the copy source</param>
/// <param name="dstAddress">Physical address of the copy destination</param>
/// <param name="size">Size in bytes of the copy</param>
private void CopyBufferSingleRange(MemoryManager memoryManager, ulong srcAddress, ulong dstAddress, ulong size)
private static void CopyBufferSingleRange(
GpuContext context,
PhysicalMemory srcPhysical,
PhysicalMemory dstPhysical,
ulong srcAddress,
ulong dstAddress,
ulong size)
{
Buffer srcBuffer = GetBuffer(srcAddress, size, BufferStage.Copy);
Buffer dstBuffer = GetBuffer(dstAddress, size, BufferStage.Copy);
Buffer srcBuffer = srcPhysical.BufferCache.GetBuffer(srcAddress, size, BufferStage.Copy);
Buffer dstBuffer = dstPhysical.BufferCache.GetBuffer(dstAddress, size, BufferStage.Copy);
int srcOffset = (int)(srcAddress - srcBuffer.Address);
int dstOffset = (int)(dstAddress - dstBuffer.Address);
_context.Renderer.Pipeline.CopyBuffer(
context.Renderer.Pipeline.CopyBuffer(
srcBuffer.Handle,
dstBuffer.Handle,
srcOffset,
@@ -820,7 +832,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
// Optimization: If the data being copied is already in memory, then copy it directly instead of flushing from GPU.
dstBuffer.ClearModified(dstAddress, size);
memoryManager.Physical.WriteTrackedResource(dstAddress, memoryManager.Physical.GetSpan(srcAddress, (int)size), ResourceKind.Buffer);
dstPhysical.WriteTrackedResource(dstAddress, srcPhysical.GetSpan(srcAddress, (int)size), ResourceKind.Buffer);
}
dstBuffer.CopyToDependantVirtualBuffers(dstAddress, size);
@@ -849,7 +861,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
_context.Renderer.Pipeline.ClearBuffer(buffer.Handle, offset, (int)subRange.Size, value);
memoryManager.Physical.FillTrackedResource(subRange.Address, subRange.Size, value, ResourceKind.Buffer);
memoryManager.GetBackingMemory(gpuVa).FillTrackedResource(subRange.Address, subRange.Size, value, ResourceKind.Buffer);
buffer.CopyToDependantVirtualBuffers(subRange.Address, subRange.Size);
}

View File

@@ -66,18 +66,19 @@ namespace Ryujinx.Graphics.Gpu.Memory
Buffers = new BufferBounds[count];
Unaligned = new bool[count];
Buffers.AsSpan().Fill(new BufferBounds(new MultiRange(MemoryManager.PteUnmapped, 0UL)));
Buffers.AsSpan().Fill(new BufferBounds(null, new MultiRange(MemoryManager.PteUnmapped, 0UL)));
}
/// <summary>
/// Sets the region of a buffer at a given slot.
/// </summary>
/// <param name="index">Buffer slot</param>
/// <param name="physical">Physical memory backing the buffer</param>
/// <param name="range">Physical memory regions where the buffer is mapped</param>
/// <param name="flags">Buffer usage flags</param>
public void SetBounds(int index, MultiRange range, BufferUsageFlags flags = BufferUsageFlags.None)
public void SetBounds(int index, PhysicalMemory physical, MultiRange range, BufferUsageFlags flags = BufferUsageFlags.None)
{
Buffers[index] = new BufferBounds(range, flags);
Buffers[index] = new BufferBounds(physical, range, flags);
}
/// <summary>
@@ -156,8 +157,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="type">Type of each index buffer element</param>
public void SetIndexBuffer(ulong gpuVa, ulong size, IndexType type)
{
MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size, BufferStage.IndexBuffer);
BufferCache bufferCache = _channel.MemoryManager.GetBackingMemory(gpuVa).BufferCache;
MultiRange range = bufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size, BufferStage.IndexBuffer);
_indexBuffer.BufferCache = bufferCache;
_indexBuffer.Range = range;
_indexBuffer.Type = type;
@@ -186,11 +189,15 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="divisor">Vertex divisor of the buffer, for instanced draws</param>
public void SetVertexBuffer(int index, ulong gpuVa, ulong size, int stride, int divisor)
{
MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size, BufferStage.VertexBuffer);
BufferCache bufferCache = _channel.MemoryManager.GetBackingMemory(gpuVa).BufferCache;
MultiRange range = bufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size, BufferStage.VertexBuffer);
_vertexBuffers[index].Range = range;
_vertexBuffers[index].Stride = stride;
_vertexBuffers[index].Divisor = divisor;
ref VertexBuffer vb = ref _vertexBuffers[index];
vb.BufferCache = bufferCache;
vb.Range = range;
vb.Stride = stride;
vb.Divisor = divisor;
_vertexBuffersDirty = true;
@@ -213,9 +220,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="size">Size in bytes of the transform feedback buffer</param>
public void SetTransformFeedbackBuffer(int index, ulong gpuVa, ulong size)
{
MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateMultiBuffers(_channel.MemoryManager, gpuVa, size, BufferStage.TransformFeedback);
PhysicalMemory physical = _channel.MemoryManager.GetBackingMemory(gpuVa);
MultiRange range = physical.BufferCache.TranslateAndCreateMultiBuffers(_channel.MemoryManager, gpuVa, size, BufferStage.TransformFeedback);
_transformFeedbackBuffers[index] = new BufferBounds(range);
_transformFeedbackBuffers[index] = new BufferBounds(physical, range);
_transformFeedbackBuffersDirty = true;
}
@@ -258,11 +266,12 @@ namespace Ryujinx.Graphics.Gpu.Memory
RecordStorageAlignment(_cpStorageBuffers, index, gpuVa);
gpuVa = BitUtils.AlignDown<ulong>(gpuVa, (ulong)_context.Capabilities.StorageBufferOffsetAlignment);
gpuVa = BitUtils.AlignDown(gpuVa, (ulong)_context.Capabilities.StorageBufferOffsetAlignment);
MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateMultiBuffers(_channel.MemoryManager, gpuVa, size, BufferStageUtils.ComputeStorage(flags));
PhysicalMemory physical = _channel.MemoryManager.GetBackingMemory(gpuVa);
MultiRange range = physical.BufferCache.TranslateAndCreateMultiBuffers(_channel.MemoryManager, gpuVa, size, BufferStageUtils.ComputeStorage(flags));
_cpStorageBuffers.SetBounds(index, range, flags);
_cpStorageBuffers.SetBounds(index, physical, range, flags);
}
/// <summary>
@@ -282,16 +291,17 @@ namespace Ryujinx.Graphics.Gpu.Memory
RecordStorageAlignment(buffers, index, gpuVa);
gpuVa = BitUtils.AlignDown<ulong>(gpuVa, (ulong)_context.Capabilities.StorageBufferOffsetAlignment);
gpuVa = BitUtils.AlignDown(gpuVa, (ulong)_context.Capabilities.StorageBufferOffsetAlignment);
MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateMultiBuffers(_channel.MemoryManager, gpuVa, size, BufferStageUtils.GraphicsStorage(stage, flags));
PhysicalMemory physical = _channel.MemoryManager.GetBackingMemory(gpuVa);
MultiRange range = physical.BufferCache.TranslateAndCreateMultiBuffers(_channel.MemoryManager, gpuVa, size, BufferStageUtils.GraphicsStorage(stage, flags));
if (!buffers.Buffers[index].Range.Equals(range))
{
_gpStorageBuffersDirty = true;
}
buffers.SetBounds(index, range, flags);
buffers.SetBounds(index, physical, range, flags);
}
/// <summary>
@@ -303,9 +313,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="size">Size in bytes of the storage buffer</param>
public void SetComputeUniformBuffer(int index, ulong gpuVa, ulong size)
{
MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size, BufferStage.Compute);
PhysicalMemory physical = _channel.MemoryManager.GetBackingMemory(gpuVa);
MultiRange range = physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size, BufferStage.Compute);
_cpUniformBuffers.SetBounds(index, range);
_cpUniformBuffers.SetBounds(index, physical, range);
}
/// <summary>
@@ -318,9 +329,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="size">Size in bytes of the storage buffer</param>
public void SetGraphicsUniformBuffer(int stage, int index, ulong gpuVa, ulong size)
{
MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size, BufferStageUtils.FromShaderStage(stage));
PhysicalMemory physical = _channel.MemoryManager.GetBackingMemory(gpuVa);
MultiRange range = _channel.MemoryManager.GetBackingMemory(gpuVa).BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size, BufferStageUtils.FromShaderStage(stage));
_gpUniformBuffers[stage].SetBounds(index, range);
_gpUniformBuffers[stage].SetBounds(index, physical, range);
_gpUniformBuffersDirty = true;
}
@@ -416,9 +428,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary>
/// <param name="index">Index of the uniform buffer binding</param>
/// <returns>The uniform buffer address, or an undefined value if the buffer is not currently bound</returns>
public ulong GetComputeUniformBufferAddress(int index)
public (PhysicalMemory, ulong) GetComputeUniformBufferAddress(int index)
{
return _cpUniformBuffers.Buffers[index].Range.GetSubRange(0).Address;
ref BufferBounds buffer = ref _cpUniformBuffers.Buffers[index];
return (buffer.Physical, buffer.Range.GetSubRange(0).Address);
}
/// <summary>
@@ -437,9 +450,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="stage">Index of the shader stage</param>
/// <param name="index">Index of the uniform buffer binding</param>
/// <returns>The uniform buffer address, or an undefined value if the buffer is not currently bound</returns>
public ulong GetGraphicsUniformBufferAddress(int stage, int index)
public (PhysicalMemory, ulong) GetGraphicsUniformBufferAddress(int stage, int index)
{
return _gpUniformBuffers[stage].Buffers[index].Range.GetSubRange(0).Address;
ref BufferBounds buffer = ref _gpUniformBuffers[stage].Buffers[index];
return (buffer.Physical, buffer.Range.GetSubRange(0).Address);
}
/// <summary>
@@ -478,12 +492,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary>
public void CommitComputeBindings()
{
BufferCache bufferCache = _channel.MemoryManager.Physical.BufferCache;
BindBuffers(_cpStorageBuffers, isStorage: true);
BindBuffers(_cpUniformBuffers, isStorage: false);
BindBuffers(bufferCache, _cpStorageBuffers, isStorage: true);
BindBuffers(bufferCache, _cpUniformBuffers, isStorage: false);
CommitBufferTextureBindings(bufferCache);
CommitBufferTextureBindings();
// Force rebind after doing compute work.
Rebind();
@@ -495,14 +507,14 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// Commit any queued buffer texture bindings.
/// </summary>
/// <param name="bufferCache">Buffer cache</param>
private void CommitBufferTextureBindings(BufferCache bufferCache)
private void CommitBufferTextureBindings()
{
if (_bufferTextures.Count > 0)
{
foreach (BufferTextureBinding binding in _bufferTextures)
{
bool isStore = binding.BindingInfo.Flags.HasFlag(TextureUsageFlags.ImageStore);
BufferRange range = bufferCache.GetBufferRange(binding.Range, BufferStageUtils.TextureBuffer(binding.Stage, binding.BindingInfo.Flags), isStore);
BufferRange range = binding.BufferCache.GetBufferRange(binding.Range, BufferStageUtils.TextureBuffer(binding.Stage, binding.BindingInfo.Flags), isStore);
binding.Texture.SetStorage(range);
// The texture must be rebound to use the new storage if it was updated.
@@ -526,7 +538,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
foreach (BufferTextureArrayBinding<ITextureArray> binding in _bufferTextureArrays)
{
BufferRange range = bufferCache.GetBufferRange(binding.Range, BufferStage.None);
BufferRange range = binding.BufferCache.GetBufferRange(binding.Range, BufferStage.None);
binding.Texture.SetStorage(range);
textureArray[0] = binding.Texture;
@@ -536,7 +548,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
foreach (BufferTextureArrayBinding<IImageArray> binding in _bufferImageArrays)
{
bool isStore = binding.BindingInfo.Flags.HasFlag(TextureUsageFlags.ImageStore);
BufferRange range = bufferCache.GetBufferRange(binding.Range, BufferStage.None, isStore);
BufferRange range = binding.BufferCache.GetBufferRange(binding.Range, BufferStage.None, isStore);
binding.Texture.SetStorage(range);
textureArray[0] = binding.Texture;
@@ -555,8 +567,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="indexed">True if the index buffer is in use</param>
public void CommitGraphicsBindings(bool indexed)
{
BufferCache bufferCache = _channel.MemoryManager.Physical.BufferCache;
if (indexed)
{
if (_indexBufferDirty || _rebind)
@@ -565,14 +575,14 @@ namespace Ryujinx.Graphics.Gpu.Memory
if (!_indexBuffer.Range.IsUnmapped)
{
BufferRange buffer = bufferCache.GetBufferRange(_indexBuffer.Range, BufferStage.IndexBuffer);
BufferRange buffer = _indexBuffer.BufferCache.GetBufferRange(_indexBuffer.Range, BufferStage.IndexBuffer);
_context.Renderer.Pipeline.SetIndexBuffer(buffer, _indexBuffer.Type);
}
}
else if (!_indexBuffer.Range.IsUnmapped)
{
bufferCache.SynchronizeBufferRange(_indexBuffer.Range);
_indexBuffer.BufferCache.SynchronizeBufferRange(_indexBuffer.Range);
}
}
else if (_rebind)
@@ -597,7 +607,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
continue;
}
BufferRange buffer = bufferCache.GetBufferRange(vb.Range, BufferStage.VertexBuffer);
BufferRange buffer = vb.BufferCache.GetBufferRange(vb.Range, BufferStage.VertexBuffer);
vertexBuffers[index] = new VertexBufferDescriptor(buffer, vb.Stride, vb.Divisor);
}
@@ -615,7 +625,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
continue;
}
bufferCache.SynchronizeBufferRange(vb.Range);
vb.BufferCache.SynchronizeBufferRange(vb.Range);
}
}
@@ -637,7 +647,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
continue;
}
tfbs[index] = bufferCache.GetBufferRange(tfb.Range, BufferStage.TransformFeedback, write: true);
tfbs[index] = tfb.BufferCache.GetBufferRange(tfb.Range, BufferStage.TransformFeedback, write: true);
}
_context.Renderer.Pipeline.SetTransformFeedbackBuffers(tfbs);
@@ -684,7 +694,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
_context.SupportBufferUpdater.SetTfeOffset(index, tfeOffset);
buffers[index] = new BufferAssignment(index, bufferCache.GetBufferRange(range, BufferStage.TransformFeedback, write: true));
buffers[index] = new BufferAssignment(index, tfb.BufferCache.GetBufferRange(range, BufferStage.TransformFeedback, write: true));
}
}
@@ -702,7 +712,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
continue;
}
bufferCache.SynchronizeBufferRange(tfb.Range);
tfb.BufferCache.SynchronizeBufferRange(tfb.Range);
}
}
@@ -710,7 +720,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
{
_gpStorageBuffersDirty = false;
BindBuffers(bufferCache, _gpStorageBuffers, isStorage: true);
BindBuffers(_gpStorageBuffers, isStorage: true);
}
else
{
@@ -721,14 +731,14 @@ namespace Ryujinx.Graphics.Gpu.Memory
{
_gpUniformBuffersDirty = false;
BindBuffers(bufferCache, _gpUniformBuffers, isStorage: false);
BindBuffers(_gpUniformBuffers, isStorage: false);
}
else
{
UpdateBuffers(_gpUniformBuffers);
}
CommitBufferTextureBindings(bufferCache);
CommitBufferTextureBindings();
_rebind = false;
@@ -742,7 +752,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="bindings">Buffer memory ranges to bind</param>
/// <param name="isStorage">True to bind as storage buffer, false to bind as uniform buffer</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void BindBuffers(BufferCache bufferCache, BuffersPerStage[] bindings, bool isStorage)
private void BindBuffers(BuffersPerStage[] bindings, bool isStorage)
{
int rangesCount = 0;
@@ -763,8 +773,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
{
bool isWrite = bounds.Flags.HasFlag(BufferUsageFlags.Write);
BufferRange range = isStorage
? bufferCache.GetBufferRangeAligned(bounds.Range, bufferStage | BufferStageUtils.FromUsage(bounds.Flags), isWrite)
: bufferCache.GetBufferRange(bounds.Range, bufferStage);
? bounds.BufferCache.GetBufferRangeAligned(bounds.Range, bufferStage | BufferStageUtils.FromUsage(bounds.Flags), isWrite)
: bounds.BufferCache.GetBufferRange(bounds.Range, bufferStage);
ranges[rangesCount++] = new BufferAssignment(bindingInfo.Binding, range);
}
@@ -780,11 +790,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <summary>
/// Bind respective buffer bindings on the host API.
/// </summary>
/// <param name="bufferCache">Buffer cache holding the buffers for the specified ranges</param>
/// <param name="buffers">Buffer memory ranges to bind</param>
/// <param name="isStorage">True to bind as storage buffer, false to bind as uniform buffer</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void BindBuffers(BufferCache bufferCache, BuffersPerStage buffers, bool isStorage)
private void BindBuffers(BuffersPerStage buffers, bool isStorage)
{
int rangesCount = 0;
@@ -800,8 +809,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
{
bool isWrite = bounds.Flags.HasFlag(BufferUsageFlags.Write);
BufferRange range = isStorage
? bufferCache.GetBufferRangeAligned(bounds.Range, BufferStageUtils.ComputeStorage(bounds.Flags), isWrite)
: bufferCache.GetBufferRange(bounds.Range, BufferStage.Compute);
? bounds.BufferCache.GetBufferRangeAligned(bounds.Range, BufferStageUtils.ComputeStorage(bounds.Flags), isWrite)
: bounds.BufferCache.GetBufferRange(bounds.Range, BufferStage.Compute);
ranges[rangesCount++] = new BufferAssignment(bindingInfo.Binding, range);
}
@@ -854,7 +863,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
continue;
}
_channel.MemoryManager.Physical.BufferCache.SynchronizeBufferRange(bounds.Range);
bounds.BufferCache.SynchronizeBufferRange(bounds.Range);
}
}
}
@@ -871,13 +880,14 @@ namespace Ryujinx.Graphics.Gpu.Memory
public void SetBufferTextureStorage(
ShaderStage stage,
ITexture texture,
BufferCache bufferCache,
MultiRange range,
TextureBindingInfo bindingInfo,
bool isImage)
{
_channel.MemoryManager.Physical.BufferCache.CreateBuffer(range, BufferStageUtils.TextureBuffer(stage, bindingInfo.Flags));
bufferCache.CreateBuffer(range, BufferStageUtils.TextureBuffer(stage, bindingInfo.Flags));
_bufferTextures.Add(new BufferTextureBinding(stage, texture, range, bindingInfo, isImage));
_bufferTextures.Add(new BufferTextureBinding(stage, texture, bufferCache, range, bindingInfo, isImage));
}
/// <summary>
@@ -894,13 +904,14 @@ namespace Ryujinx.Graphics.Gpu.Memory
ShaderStage stage,
ITextureArray array,
ITexture texture,
BufferCache bufferCache,
MultiRange range,
TextureBindingInfo bindingInfo,
int index)
{
_channel.MemoryManager.Physical.BufferCache.CreateBuffer(range, BufferStageUtils.TextureBuffer(stage, bindingInfo.Flags));
bufferCache.CreateBuffer(range, BufferStageUtils.TextureBuffer(stage, bindingInfo.Flags));
_bufferTextureArrays.Add(new BufferTextureArrayBinding<ITextureArray>(array, texture, range, bindingInfo, index));
_bufferTextureArrays.Add(new BufferTextureArrayBinding<ITextureArray>(array, texture, bufferCache, range, bindingInfo, index));
}
/// <summary>
@@ -917,13 +928,14 @@ namespace Ryujinx.Graphics.Gpu.Memory
ShaderStage stage,
IImageArray array,
ITexture texture,
BufferCache bufferCache,
MultiRange range,
TextureBindingInfo bindingInfo,
int index)
{
_channel.MemoryManager.Physical.BufferCache.CreateBuffer(range, BufferStageUtils.TextureBuffer(stage, bindingInfo.Flags));
bufferCache.CreateBuffer(range, BufferStageUtils.TextureBuffer(stage, bindingInfo.Flags));
_bufferImageArrays.Add(new BufferTextureArrayBinding<IImageArray>(array, texture, range, bindingInfo, index));
_bufferImageArrays.Add(new BufferTextureArrayBinding<IImageArray>(array, texture, bufferCache, range, bindingInfo, index));
}
/// <summary>

View File

@@ -19,6 +19,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary>
public ITexture Texture { get; }
/// <summary>
/// Buffer cache that owns the buffer.
/// </summary>
public BufferCache BufferCache { get; }
/// <summary>
/// Physical ranges of memory where the buffer texture data is located.
/// </summary>
@@ -39,18 +44,21 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary>
/// <param name="array">Array</param>
/// <param name="texture">Buffer texture</param>
/// <param name="bufferCache">Buffer cache that owns the buffer</param>
/// <param name="range">Physical ranges of memory where the buffer texture data is located</param>
/// <param name="bindingInfo">Binding info</param>
/// <param name="index">Index of the binding on the array</param>
public BufferTextureArrayBinding(
T array,
ITexture texture,
BufferCache bufferCache,
MultiRange range,
TextureBindingInfo bindingInfo,
int index)
{
Array = array;
Texture = texture;
BufferCache = bufferCache;
Range = range;
BindingInfo = bindingInfo;
Index = index;

View File

@@ -20,6 +20,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary>
public ITexture Texture { get; }
/// <summary>
/// Buffer cache that owns the buffer.
/// </summary>
public BufferCache BufferCache { get; }
/// <summary>
/// Physical ranges of memory where the buffer texture data is located.
/// </summary>
@@ -40,18 +45,21 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary>
/// <param name="stage">Shader stage accessing the texture</param>
/// <param name="texture">Buffer texture</param>
/// <param name="bufferCache">Buffer cache that owns the buffer</param>
/// <param name="range">Physical ranges of memory where the buffer texture data is located</param>
/// <param name="bindingInfo">Binding info</param>
/// <param name="isImage">Whether the binding is for an image or a sampler</param>
public BufferTextureBinding(
ShaderStage stage,
ITexture texture,
BufferCache bufferCache,
MultiRange range,
TextureBindingInfo bindingInfo,
bool isImage)
{
Stage = stage;
Texture = texture;
BufferCache = bufferCache;
Range = range;
BindingInfo = bindingInfo;
IsImage = isImage;

View File

@@ -8,6 +8,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary>
struct IndexBuffer
{
public BufferCache BufferCache;
public MultiRange Range;
public IndexType Type;
}

View File

@@ -1,4 +1,5 @@
using Ryujinx.Common.Memory;
using Ryujinx.Graphics.Gpu.Image;
using Ryujinx.Memory;
using Ryujinx.Memory.Range;
using System;
@@ -35,10 +36,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
public event EventHandler<UnmapEventArgs> MemoryUnmapped;
/// <summary>
/// Physical memory where the virtual memory is mapped into.
/// </summary>
internal PhysicalMemory Physical { get; }
private readonly GpuContext _context;
private readonly List<PhysicalMemory> _physicalMemoryList;
private readonly Dictionary<PhysicalMemory, byte> _physicalMemoryMap;
/// <summary>
/// Virtual range cache.
@@ -53,19 +53,65 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <summary>
/// Creates a new instance of the GPU memory manager.
/// </summary>
/// <param name="context">GPU context</param>
/// <param name="physicalMemory">Physical memory that this memory manager will map into</param>
/// <param name="cpuMemorySize">The amount of physical CPU Memory Avaiable on the device.</param>
internal MemoryManager(PhysicalMemory physicalMemory, ulong cpuMemorySize)
internal MemoryManager(GpuContext context, PhysicalMemory physicalMemory, ulong cpuMemorySize)
{
Physical = physicalMemory;
_context = context;
_physicalMemoryList = new List<PhysicalMemory>()
{
physicalMemory
};
_physicalMemoryMap = new Dictionary<PhysicalMemory, byte>
{
{ physicalMemory, 0 }
};
VirtualRangeCache = new VirtualRangeCache(this);
CounterCache = new CounterCache();
_pageTable = new ulong[PtLvl0Size][];
MemoryUnmapped += Physical.TextureCache.MemoryUnmappedHandler;
MemoryUnmapped += Physical.BufferCache.MemoryUnmappedHandler;
MemoryUnmapped += physicalMemory.TextureCache.MemoryUnmappedHandler;
MemoryUnmapped += physicalMemory.BufferCache.MemoryUnmappedHandler;
MemoryUnmapped += VirtualRangeCache.MemoryUnmappedHandler;
MemoryUnmapped += CounterCache.MemoryUnmappedHandler;
Physical.TextureCache.Initialize(cpuMemorySize);
physicalMemory.TextureCache.Initialize(cpuMemorySize);
}
/// <summary>
/// Attaches the memory manager to a new GPU channel.
/// </summary>
/// <param name="rebind">Action to be performed when the buffer cache changes</param>
internal void AttachToChannel(Action rebind)
{
PhysicalMemory physicalMemory = GetOwnPhysicalMemory();
physicalMemory.IncrementReferenceCount();
physicalMemory.BufferCache.NotifyBuffersModified += rebind;
physicalMemory.BufferCache.QueuePrune();
}
/// <summary>
/// Attaches the memory manager to a new GPU channel.
/// </summary>
/// <param name="rebind">Action that was performed when the buffer cache changed</param>
internal void DetachFromChannel(Action rebind)
{
PhysicalMemory physicalMemory = GetOwnPhysicalMemory();
physicalMemory.BufferCache.NotifyBuffersModified -= rebind;
physicalMemory.DecrementReferenceCount();
}
/// <summary>
/// Queues a prune of invalid entries on the buffer cache.
/// </summary>
internal void QueuePrune()
{
GetOwnPhysicalMemory().BufferCache.QueuePrune();
}
/// <summary>
@@ -81,15 +127,15 @@ namespace Ryujinx.Graphics.Gpu.Memory
if (IsContiguous(va, size))
{
ulong address = Translate(va);
(PhysicalMemory physicalMemory, ulong address) = TranslateWithPhysicalMemory(va);
if (tracked)
{
return Physical.ReadTracked<T>(address);
return physicalMemory.ReadTracked<T>(address);
}
else
{
return Physical.Read<T>(address);
return physicalMemory.Read<T>(address);
}
}
else
@@ -113,7 +159,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
{
if (IsContiguous(va, size))
{
return Physical.GetSpan(Translate(va), size, tracked);
(PhysicalMemory physicalMemory, ulong address) = TranslateWithPhysicalMemory(va);
return physicalMemory.GetSpan(address, size, tracked);
}
else
{
@@ -138,7 +186,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
bool isContiguous = true;
int mappedSize;
if (ValidateAddress(va) && GetPte(va) != PteUnmapped && Physical.IsMapped(Translate(va)))
if (ValidateAddress(va) && IsMappedOnGpuAndPhysical(va))
{
ulong endVa = va + (ulong)size;
ulong endVaAligned = (endVa + PageMask) & ~PageMask;
@@ -151,7 +199,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
ulong nextVa = currentVa + PageSize;
ulong nextPa = Translate(nextVa);
if (!ValidateAddress(nextVa) || GetPte(nextVa) == PteUnmapped || !Physical.IsMapped(nextPa))
if (!ValidateAddress(nextVa) || !IsMappedOnGpuAndPhysical(nextVa))
{
break;
}
@@ -180,7 +228,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
if (isContiguous)
{
return Physical.GetSpan(Translate(va), mappedSize, tracked);
(PhysicalMemory physicalMemory, ulong address) = TranslateWithPhysicalMemory(va);
return physicalMemory.GetSpan(address, mappedSize, tracked);
}
else
{
@@ -192,6 +242,23 @@ namespace Ryujinx.Graphics.Gpu.Memory
}
}
/// <summary>
/// Checks if a page of memory is mapped on the GPU and its backing memory.
/// </summary>
/// <param name="va">GPU virtual address of the page</param>
/// <returns>True if mapped, false otherwise</returns>
private bool IsMappedOnGpuAndPhysical(ulong va)
{
(PhysicalMemory physicalMemory, ulong address) = TranslateWithPhysicalMemory(va);
if (address == PteUnmapped)
{
return false;
}
return physicalMemory.IsMapped(address);
}
/// <summary>
/// Reads data from a possibly non-contiguous region of GPU mapped memory.
/// </summary>
@@ -209,22 +276,22 @@ namespace Ryujinx.Graphics.Gpu.Memory
if ((va & PageMask) != 0)
{
ulong pa = Translate(va);
(PhysicalMemory physicalMemory, ulong pa) = TranslateWithPhysicalMemory(va);
size = Math.Min(data.Length, (int)PageSize - (int)(va & PageMask));
Physical.GetSpan(pa, size, tracked).CopyTo(data[..size]);
physicalMemory.GetSpan(pa, size, tracked).CopyTo(data[..size]);
offset += size;
}
for (; offset < data.Length; offset += size)
{
ulong pa = Translate(va + (ulong)offset);
(PhysicalMemory physicalMemory, ulong pa) = TranslateWithPhysicalMemory(va + (ulong)offset);
size = Math.Min(data.Length - offset, (int)PageSize);
Physical.GetSpan(pa, size, tracked).CopyTo(data.Slice(offset, size));
physicalMemory.GetSpan(pa, size, tracked).CopyTo(data.Slice(offset, size));
}
}
@@ -239,15 +306,17 @@ namespace Ryujinx.Graphics.Gpu.Memory
{
if (IsContiguous(va, size))
{
return Physical.GetWritableRegion(Translate(va), size, tracked);
(PhysicalMemory physicalMemory, ulong address) = TranslateWithPhysicalMemory(va);
return physicalMemory.GetWritableRegion(address, size, tracked);
}
else
{
MemoryOwner<byte> memoryOwner = MemoryOwner<byte>.Rent(size);
Memory<byte> memory = new byte[size];
ReadImpl(va, memoryOwner.Span, tracked);
GetSpan(va, size).CopyTo(memory.Span);
return new WritableRegion(this, va, memoryOwner, tracked);
return new WritableRegion(this, va, memory, tracked);
}
}
@@ -269,7 +338,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="data">The data to be written</param>
public void Write(ulong va, ReadOnlySpan<byte> data)
{
WriteImpl(va, data, Physical.Write);
WriteImpl(va, data, (physical, va, data) => physical.Write(va, data));
}
/// <summary>
@@ -279,7 +348,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="data">The data to be written</param>
public void WriteTrackedResource(ulong va, ReadOnlySpan<byte> data)
{
WriteImpl(va, data, Physical.WriteTrackedResource);
WriteImpl(va, data, (physical, va, data) => physical.WriteTrackedResource(va, data));
}
/// <summary>
@@ -289,10 +358,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="data">The data to be written</param>
public void WriteUntracked(ulong va, ReadOnlySpan<byte> data)
{
WriteImpl(va, data, Physical.WriteUntracked);
WriteImpl(va, data, (physical, va, data) => physical.WriteUntracked(va, data));
}
private delegate void WriteCallback(ulong address, ReadOnlySpan<byte> data);
private delegate void WriteCallback(PhysicalMemory physicalMemory, ulong address, ReadOnlySpan<byte> data);
/// <summary>
/// Writes data to possibly non-contiguous GPU mapped memory.
@@ -304,7 +373,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
{
if (IsContiguous(va, data.Length))
{
writeCallback(Translate(va), data);
(PhysicalMemory physicalMemory, ulong address) = TranslateWithPhysicalMemory(va);
writeCallback(physicalMemory, address, data);
}
else
{
@@ -312,22 +383,67 @@ namespace Ryujinx.Graphics.Gpu.Memory
if ((va & PageMask) != 0)
{
ulong pa = Translate(va);
(PhysicalMemory physicalMemory, ulong pa) = TranslateWithPhysicalMemory(va);
size = Math.Min(data.Length, (int)PageSize - (int)(va & PageMask));
writeCallback(pa, data[..size]);
writeCallback(physicalMemory, pa, data[..size]);
offset += size;
}
for (; offset < data.Length; offset += size)
{
ulong pa = Translate(va + (ulong)offset);
(PhysicalMemory physicalMemory, ulong pa) = TranslateWithPhysicalMemory(va + (ulong)offset);
size = Math.Min(data.Length - offset, (int)PageSize);
writeCallback(pa, data.Slice(offset, size));
writeCallback(physicalMemory, pa, data.Slice(offset, size));
}
}
}
/// <summary>
/// Writes data to GPU mapped memory, stopping at the first unmapped page at the memory region, if any.
/// </summary>
/// <param name="va">GPU virtual address to write the data into</param>
/// <param name="data">The data to be written</param>
public void WriteMapped(ulong va, ReadOnlySpan<byte> data)
{
if (IsContiguous(va, data.Length))
{
(PhysicalMemory physicalMemory, ulong address) = TranslateWithPhysicalMemory(va);
physicalMemory.Write(address, data);
}
else
{
int offset = 0, size;
if ((va & PageMask) != 0)
{
(PhysicalMemory physicalMemory, ulong pa) = TranslateWithPhysicalMemory(va);
size = Math.Min(data.Length, (int)PageSize - (int)(va & PageMask));
if (pa != PteUnmapped && physicalMemory.IsMapped(pa))
{
physicalMemory.Write(pa, data[..size]);
}
offset += size;
}
for (; offset < data.Length; offset += size)
{
(PhysicalMemory physicalMemory, ulong pa) = TranslateWithPhysicalMemory(va + (ulong)offset);
size = Math.Min(data.Length - offset, (int)PageSize);
if (pa != PteUnmapped && physicalMemory.IsMapped(pa))
{
physicalMemory.Write(pa, data.Slice(offset, size));
}
}
}
}
@@ -359,15 +475,51 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="size">Size in bytes of the mapping</param>
/// <param name="kind">Kind of the resource located at the mapping</param>
public void Map(ulong pa, ulong va, ulong size, PteKind kind)
{
MapImpl(pa, va, size, kind);
}
/// <summary>
/// Maps a given range of pages to the specified CPU virtual address from a different process.
/// </summary>
/// <remarks>
/// All addresses and sizes must be page aligned.
/// </remarks>
/// <param name="pa">CPU virtual address to map into</param>
/// <param name="va">GPU virtual address to be mapped</param>
/// <param name="size">Size in bytes of the mapping</param>
/// <param name="kind">Kind of the resource located at the mapping</param>
/// <param name="ownedPid">PID of the process that owns the mapping</param>
public void MapForeign(ulong pa, ulong va, ulong size, PteKind kind, ulong ownedPid)
{
if (_context.PhysicalMemoryRegistry.TryGetValue(ownedPid, out PhysicalMemory physicalMemory))
{
MapImpl(pa, va, size, kind, physicalMemory);
}
}
/// <summary>
/// Maps a given range of pages to the specified CPU virtual address.
/// </summary>
/// <remarks>
/// All addresses and sizes must be page aligned.
/// </remarks>
/// <param name="pa">CPU virtual address to map into</param>
/// <param name="va">GPU virtual address to be mapped</param>
/// <param name="size">Size in bytes of the mapping</param>
/// <param name="kind">Kind of the resource located at the mapping</param>
/// <param name="physicalMemory">Optional physical memory to import for the mapping</param>
private void MapImpl(ulong pa, ulong va, ulong size, PteKind kind, PhysicalMemory physicalMemory = null)
{
lock (_pageTable)
{
UnmapEventArgs e = new(va, size);
MemoryUnmapped?.Invoke(this, e);
byte pIndex = physicalMemory != null ? GetOrAddPhysicalMemory(physicalMemory) : (byte)0;
for (ulong offset = 0; offset < size; offset += PageSize)
{
SetPte(va + offset, PackPte(pa + offset, kind));
SetPte(va + offset, PackPte(pa + offset, pIndex, kind));
}
RunRemapActions(e);
@@ -418,12 +570,14 @@ namespace Ryujinx.Graphics.Gpu.Memory
for (int page = 0; page < pages - 1; page++)
{
if (!ValidateAddress(va + PageSize) || GetPte(va + PageSize) == PteUnmapped)
ulong nextPte = GetPte(va + PageSize);
if (!ValidateAddress(va + PageSize) || nextPte == PteUnmapped)
{
return false;
}
if (Translate(va) + PageSize != Translate(va + PageSize))
if (GetPte(va) + PageSize != nextPte)
{
return false;
}
@@ -457,7 +611,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
int pages = (int)((endVaRounded - va) / PageSize);
List<MemoryRange> regions = [];
List<MemoryRange> regions = new();
for (int page = 0; page < pages - 1; page++)
{
@@ -535,6 +689,49 @@ namespace Ryujinx.Graphics.Gpu.Memory
return true;
}
/// <summary>
/// Gets the backing memory for a given GPU virtual address.
/// </summary>
/// <param name="va">GPU virtual address to get the backing memory from</param>
/// <returns>The backing memory for the specified GPU virtual address</returns>
internal PhysicalMemory GetBackingMemory(ulong va)
{
ulong pte = GetPte(va);
if (pte == PteUnmapped)
{
return GetOwnPhysicalMemory();
}
return _physicalMemoryList[UnpackPIndexFromPte(pte)];
}
/// <summary>
/// Gets the backing memory that is owned by this GPU memory manager.
/// </summary>
/// <returns>The backing memory owned by this memory manager</returns>
private PhysicalMemory GetOwnPhysicalMemory()
{
return _physicalMemoryList[0];
}
/// <summary>
/// Gets the index for a given physical memory on the list, adding it to the list if needed.
/// </summary>
/// <param name="physicalMemory">Physical memory to get the index from</param>
/// <returns>The index of the physical memory on the list</returns>
private byte GetOrAddPhysicalMemory(PhysicalMemory physicalMemory)
{
if (!_physicalMemoryMap.TryGetValue(physicalMemory, out byte pIndex))
{
pIndex = checked((byte)_physicalMemoryList.Count);
_physicalMemoryList.Add(physicalMemory);
_physicalMemoryMap.Add(physicalMemory, pIndex);
}
return pIndex;
}
/// <summary>
/// Validates a GPU virtual address.
/// </summary>
@@ -636,6 +833,28 @@ namespace Ryujinx.Graphics.Gpu.Memory
return Math.Min(maxSize, va - startVa);
}
/// <summary>
/// Translates a GPU virtual address to a CPU virtual address and the associated physical memory.
/// </summary>
/// <param name="va">GPU virtual address to be translated</param>
/// <returns>CPU virtual address with the physical memory, or <see cref="PteUnmapped"/> if unmapped</returns>
private (PhysicalMemory, ulong) TranslateWithPhysicalMemory(ulong va)
{
if (!ValidateAddress(va))
{
return (GetOwnPhysicalMemory(), PteUnmapped);
}
ulong pte = GetPte(va);
if (pte == PteUnmapped)
{
return (GetOwnPhysicalMemory(), PteUnmapped);
}
return (_physicalMemoryList[UnpackPIndexFromPte(pte)], UnpackPaFromPte(pte) + (va & PageMask));
}
/// <summary>
/// Gets the kind of a given memory page.
/// This might indicate the type of resource that can be allocated on the page, and also texture tiling.
@@ -659,6 +878,18 @@ namespace Ryujinx.Graphics.Gpu.Memory
return UnpackKindFromPte(pte);
}
public bool IsForeignMapping(ulong va)
{
ulong pte = GetPte(va);
if (pte == PteUnmapped)
{
return false;
}
return UnpackPIndexFromPte(pte) != 0;
}
/// <summary>
/// Gets the Page Table entry for a given GPU virtual address.
/// </summary>
@@ -704,11 +935,12 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// Creates a page table entry from a physical address and kind.
/// </summary>
/// <param name="pa">Physical address</param>
/// <param name="pIndex">Index of the physical memory on the list</param>
/// <param name="kind">Kind</param>
/// <returns>Page table entry</returns>
private static ulong PackPte(ulong pa, PteKind kind)
private static ulong PackPte(ulong pa, byte pIndex, PteKind kind)
{
return pa | ((ulong)kind << 56);
return pa | ((ulong)pIndex << 48) | ((ulong)kind << 56);
}
/// <summary>
@@ -721,6 +953,16 @@ namespace Ryujinx.Graphics.Gpu.Memory
return (PteKind)(pte >> 56);
}
/// <summary>
/// Unpacks the physical memory index in the list from a page table entry.
/// </summary>
/// <param name="pte">Page table entry</param>
/// <returns>Physical memory index</returns>
private static byte UnpackPIndexFromPte(ulong pte)
{
return (byte)(pte >> 48);
}
/// <summary>
/// Unpacks physical address from a page table entry.
/// </summary>
@@ -728,7 +970,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <returns>Physical address</returns>
private static ulong UnpackPaFromPte(ulong pte)
{
return pte & 0xffffffffffffffUL;
return pte & 0xffffffffffffUL;
}
}
}

View File

@@ -7,6 +7,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary>
struct VertexBuffer
{
public BufferCache BufferCache;
public MultiRange Range;
public int Stride;
public int Divisor;

View File

@@ -3,7 +3,6 @@
<PropertyGroup>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
<Configurations>Debug;Release;DebugAOT;ReleaseAOT</Configurations>
</PropertyGroup>
<ItemGroup>

View File

@@ -324,6 +324,11 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
bool loadHostCache = header.CodeGenVersion == CodeGenVersion;
if (context.Capabilities.Api == TargetApi.Metal)
{
loadHostCache = false;
}
int programIndex = 0;
DataEntry entry = new();
@@ -392,7 +397,8 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
context,
shaders,
specState.PipelineState,
specState.TransformFeedbackDescriptors != null);
specState.TransformFeedbackDescriptors != null,
specState.ComputeState.GetLocalSize());
IProgram hostProgram;
@@ -629,7 +635,10 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
return;
}
WriteHostCode(context, hostCode, program.Shaders, streams, timestamp);
if (context.Capabilities.Api != TargetApi.Metal)
{
WriteHostCode(context, hostCode, program.Shaders, streams, timestamp);
}
}
/// <summary>

View File

@@ -1,3 +1,4 @@
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Shader;
@@ -366,6 +367,9 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
{
try
{
if (_context.Capabilities.Api == TargetApi.Metal && _context.DirtyHacks.IsEnabled(DirtyHack.ShaderTranslationDelay))
Thread.Sleep(_context.DirtyHacks[DirtyHack.ShaderTranslationDelay]);
AsyncProgramTranslation asyncTranslation = new(guestShaders, specState, programIndex, isCompute);
_asyncTranslationQueue.Add(asyncTranslation, _cancellationToken);
}
@@ -490,7 +494,12 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
{
ShaderSource[] shaderSources = new ShaderSource[compilation.TranslatedStages.Length];
ShaderInfoBuilder shaderInfoBuilder = new(_context, compilation.SpecializationState.TransformFeedbackDescriptors != null);
ref GpuChannelComputeState computeState = ref compilation.SpecializationState.ComputeState;
ShaderInfoBuilder shaderInfoBuilder = new(
_context,
compilation.SpecializationState.TransformFeedbackDescriptors != null,
computeLocalSize: computeState.GetLocalSize());
for (int index = 0; index < compilation.TranslatedStages.Length; index++)
{
@@ -720,7 +729,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
ShaderProgram program = translatorContext.Translate();
CachedShaderStage[] shaders = [new(program.Info, shader.Code, shader.Cb1Data)];
CachedShaderStage[] shaders = [new CachedShaderStage(program.Info, shader.Code, shader.Cb1Data)];
_compilationQueue.Enqueue(new ProgramCompilation([program], shaders, newSpecState, programIndex, isCompute: true));
}

View File

@@ -1,5 +1,6 @@
using Ryujinx.Common.Logging;
using Ryujinx.Graphics.Gpu.Image;
using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Shader;
using Ryujinx.Graphics.Shader.Translation;
using System;
@@ -17,7 +18,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
private readonly GpuAccessorState _state;
private readonly int _stageIndex;
private readonly bool _compute;
private readonly bool _isVulkan;
private readonly bool _isOpenGL;
private readonly bool _hasGeometryShader;
private readonly bool _supportsQuads;
@@ -39,7 +40,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
_channel = channel;
_state = state;
_stageIndex = stageIndex;
_isVulkan = context.Capabilities.Api == TargetApi.Vulkan;
_isOpenGL = context.Capabilities.Api == TargetApi.OpenGL;
_hasGeometryShader = hasGeometryShader;
_supportsQuads = context.Capabilities.SupportsQuads;
@@ -66,11 +67,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <inheritdoc/>
public uint ConstantBuffer1Read(int offset)
{
ulong baseAddress = _compute
(PhysicalMemory physical, ulong baseAddress) = _compute
? _channel.BufferManager.GetComputeUniformBufferAddress(1)
: _channel.BufferManager.GetGraphicsUniformBufferAddress(_stageIndex, 1);
return _channel.MemoryManager.Physical.Read<uint>(baseAddress + (ulong)offset);
return physical.Read<uint>(baseAddress + (ulong)offset);
}
/// <inheritdoc/>
@@ -117,10 +118,10 @@ namespace Ryujinx.Graphics.Gpu.Shader
public GpuGraphicsState QueryGraphicsState()
{
return _state.GraphicsState.CreateShaderGraphicsState(
!_isVulkan,
_isOpenGL,
_supportsQuads,
_hasGeometryShader,
_isVulkan || _state.GraphicsState.YNegateEnabled);
!_isOpenGL || _state.GraphicsState.YNegateEnabled);
}
/// <inheritdoc/>

View File

@@ -55,7 +55,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
{
int binding;
if (_context.Capabilities.Api == TargetApi.Vulkan)
if (_context.Capabilities.Api != TargetApi.OpenGL)
{
binding = GetBindingFromIndex(index, _context.Capabilities.MaximumUniformBuffersPerStage, "Uniform buffer");
}
@@ -71,7 +71,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
{
int binding;
if (_context.Capabilities.Api == TargetApi.Vulkan)
if (_context.Capabilities.Api != TargetApi.OpenGL)
{
if (count == 1)
{
@@ -103,7 +103,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
{
int binding;
if (_context.Capabilities.Api == TargetApi.Vulkan)
if (_context.Capabilities.Api != TargetApi.OpenGL)
{
binding = GetBindingFromIndex(index, _context.Capabilities.MaximumStorageBuffersPerStage, "Storage buffer");
}
@@ -119,7 +119,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
{
int binding;
if (_context.Capabilities.Api == TargetApi.Vulkan)
if (_context.Capabilities.Api != TargetApi.OpenGL)
{
if (count == 1)
{

View File

@@ -1,3 +1,5 @@
using Ryujinx.Graphics.GAL;
namespace Ryujinx.Graphics.Gpu.Shader
{
/// <summary>
@@ -61,5 +63,14 @@ namespace Ryujinx.Graphics.Gpu.Shader
SharedMemorySize = sharedMemorySize;
HasUnalignedStorageBuffer = hasUnalignedStorageBuffer;
}
/// <summary>
/// Gets the local group size of the shader in a GAL compatible struct.
/// </summary>
/// <returns>Local group size</returns>
public ComputeSize GetLocalSize()
{
return new ComputeSize(LocalSizeX, LocalSizeY, LocalSizeZ);
}
}
}

View File

@@ -117,7 +117,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
private static string GetDiskCachePath()
{
return GraphicsConfig.EnableShaderCache && GraphicsConfig.TitleId != null
? Path.Combine(AppDataManager.GamesDirPath, GraphicsConfig.TitleId, "cache", "shader")
? Path.Combine(AppDataManager.GamesDirPath, GraphicsConfig.TitleId.ToLower(), "cache", "shader")
: null;
}
@@ -204,7 +204,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
GpuChannelComputeState computeState,
ulong gpuVa)
{
if (_cpPrograms.TryGetValue(gpuVa, out var cpShader) && IsShaderEqual(channel, poolState, computeState, cpShader, gpuVa))
if (_cpPrograms.TryGetValue(gpuVa, out CachedShaderProgram cpShader) && IsShaderEqual(channel, poolState, computeState, cpShader, gpuVa))
{
return cpShader;
}
@@ -223,8 +223,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
TranslatorContext translatorContext = DecodeComputeShader(gpuAccessor, _context.Capabilities.Api, gpuVa);
TranslatedShader translatedShader = TranslateShader(_dumper, channel, translatorContext, cachedGuestCode, asCompute: false);
ShaderSource[] shaderSourcesArray = new ShaderSource[] { CreateShaderSource(translatedShader.Program) };
ShaderInfo info = ShaderInfoBuilder.BuildForCompute(_context, translatedShader.Program.Info);
ShaderSource[] shaderSourcesArray = [CreateShaderSource(translatedShader.Program)];
ShaderInfo info = ShaderInfoBuilder.BuildForCompute(
_context,
translatedShader.Program.Info,
computeState.GetLocalSize());
IProgram hostProgram = _context.Renderer.CreateProgram(shaderSourcesArray, info);
cpShader = new CachedShaderProgram(hostProgram, specState, translatedShader.Shader);
@@ -251,8 +254,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
{
channel.TextureManager.UpdateRenderTargets();
var rtControl = state.RtControl;
var msaaMode = state.RtMsaaMode;
RtControl rtControl = state.RtControl;
TextureMsaaMode msaaMode = state.RtMsaaMode;
pipeline.SamplesCount = msaaMode.SamplesInX() * msaaMode.SamplesInY();
@@ -262,7 +265,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
{
int rtIndex = rtControl.UnpackPermutationIndex(index);
var colorState = state.RtColorState[rtIndex];
RtColorState colorState = state.RtColorState[rtIndex];
if (index >= count || colorState.Format == 0 || colorState.WidthOrStride == 0)
{
@@ -307,12 +310,12 @@ namespace Ryujinx.Graphics.Gpu.Shader
ref GpuChannelGraphicsState graphicsState,
ShaderAddresses addresses)
{
if (_gpPrograms.TryGetValue(addresses, out var gpShaders) && IsShaderEqual(channel, ref poolState, ref graphicsState, gpShaders, addresses))
if (_gpPrograms.TryGetValue(addresses, out CachedShaderProgram gpShaders) && IsShaderEqual(channel, ref poolState, ref graphicsState, gpShaders, addresses))
{
return gpShaders;
}
if (_graphicsShaderCache.TryFind(channel, ref poolState, ref graphicsState, addresses, out gpShaders, out var cachedGuestCode))
if (_graphicsShaderCache.TryFind(channel, ref poolState, ref graphicsState, addresses, out gpShaders, out CachedGraphicsGuestCode cachedGuestCode))
{
_gpPrograms[addresses] = gpShaders;
return gpShaders;
@@ -365,7 +368,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
bool geometryToCompute = ShouldConvertGeometryToCompute(_context, geometryHasStore);
CachedShaderStage[] shaders = new CachedShaderStage[Constants.ShaderStages + 1];
List<ShaderSource> shaderSources = new();
List<ShaderSource> shaderSources = [];
TranslatorContext previousStage = null;
ShaderInfoBuilder infoBuilder = new(_context, transformFeedbackDescriptors != null, vertexToCompute);
@@ -425,7 +428,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
TranslatorContext lastInVertexPipeline = geometryToCompute ? translatorContexts[4] ?? currentStage : currentStage;
program = lastInVertexPipeline.GenerateVertexPassthroughForCompute();
(program, ShaderProgramInfo vacInfo) = lastInVertexPipeline.GenerateVertexPassthroughForCompute();
infoBuilder.AddStageInfoVac(vacInfo);
}
else
{
@@ -530,9 +534,9 @@ namespace Ryujinx.Graphics.Gpu.Shader
private ShaderAsCompute CreateHostVertexAsComputeProgram(ShaderProgram program, TranslatorContext context, bool tfEnabled)
{
ShaderSource source = new(program.Code, program.BinaryCode, ShaderStage.Compute, program.Language);
ShaderInfo info = ShaderInfoBuilder.BuildForVertexAsCompute(_context, program.Info, tfEnabled);
ShaderInfo info = ShaderInfoBuilder.BuildForVertexAsCompute(_context, program.Info, context.GetVertexAsComputeInfo(), tfEnabled);
return new(_context.Renderer.CreateProgram(new[] { source }, info), program.Info, context.GetResourceReservations());
return new(_context.Renderer.CreateProgram([source], info), program.Info, context.GetResourceReservations());
}
/// <summary>
@@ -582,7 +586,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
for (int i = 0; i < Constants.TotalTransformFeedbackBuffers; i++)
{
var tf = state.TfState[i];
TfState tf = state.TfState[i];
descs[i] = new TransformFeedbackDescriptor(
tf.BufferIndex,
@@ -688,7 +692,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <returns>The generated translator context</returns>
public static TranslatorContext DecodeComputeShader(IGpuAccessor gpuAccessor, TargetApi api, ulong gpuVa)
{
var options = CreateTranslationOptions(api, DefaultFlags | TranslationFlags.Compute);
TranslationOptions options = CreateTranslationOptions(api, DefaultFlags | TranslationFlags.Compute);
return Translator.CreateContext(gpuVa, gpuAccessor, options);
}
@@ -705,7 +709,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <returns>The generated translator context</returns>
public static TranslatorContext DecodeGraphicsShader(IGpuAccessor gpuAccessor, TargetApi api, TranslationFlags flags, ulong gpuVa)
{
var options = CreateTranslationOptions(api, flags);
TranslationOptions options = CreateTranslationOptions(api, flags);
return Translator.CreateContext(gpuVa, gpuAccessor, options);
}
@@ -729,15 +733,15 @@ namespace Ryujinx.Graphics.Gpu.Shader
byte[] codeB,
bool asCompute)
{
ulong cb1DataAddress = channel.BufferManager.GetGraphicsUniformBufferAddress(0, 1);
var memoryManager = channel.MemoryManager;
(PhysicalMemory physical, ulong cb1DataAddress) = channel.BufferManager.GetGraphicsUniformBufferAddress(0, 1);
MemoryManager memoryManager = channel.MemoryManager;
codeA ??= memoryManager.GetSpan(vertexA.Address, vertexA.Size).ToArray();
codeB ??= memoryManager.GetSpan(currentStage.Address, currentStage.Size).ToArray();
byte[] cb1DataA = ReadArray(memoryManager, cb1DataAddress, vertexA.Cb1DataSize);
byte[] cb1DataB = ReadArray(memoryManager, cb1DataAddress, currentStage.Cb1DataSize);
byte[] cb1DataA = ReadArray(physical, cb1DataAddress, vertexA.Cb1DataSize);
byte[] cb1DataB = ReadArray(physical, cb1DataAddress, currentStage.Cb1DataSize);
ShaderDumpPaths pathsA = default;
ShaderDumpPaths pathsB = default;
@@ -769,13 +773,13 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <returns>Compiled graphics shader code</returns>
private static TranslatedShader TranslateShader(ShaderDumper dumper, GpuChannel channel, TranslatorContext context, byte[] code, bool asCompute)
{
var memoryManager = channel.MemoryManager;
MemoryManager memoryManager = channel.MemoryManager;
ulong cb1DataAddress = context.Stage == ShaderStage.Compute
(PhysicalMemory physical, ulong cb1DataAddress) = context.Stage == ShaderStage.Compute
? channel.BufferManager.GetComputeUniformBufferAddress(1)
: channel.BufferManager.GetGraphicsUniformBufferAddress(StageToStageIndex(context.Stage), 1);
byte[] cb1Data = ReadArray(memoryManager, cb1DataAddress, context.Cb1DataSize);
byte[] cb1Data = ReadArray(physical, cb1DataAddress, context.Cb1DataSize);
code ??= memoryManager.GetSpan(context.Address, context.Size).ToArray();
ShaderDumpPaths paths = dumper?.Dump(code, context.Stage == ShaderStage.Compute) ?? default;
@@ -789,18 +793,18 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <summary>
/// Reads data from physical memory, returns an empty array if the memory is unmapped or size is 0.
/// </summary>
/// <param name="memoryManager">Memory manager with the physical memory to read from</param>
/// <param name="physicalMemory">Physical memory to read the data from, might be null</param>
/// <param name="address">Physical address of the region to read</param>
/// <param name="size">Size in bytes of the data</param>
/// <returns>An array with the data at the specified memory location</returns>
private static byte[] ReadArray(MemoryManager memoryManager, ulong address, int size)
private static byte[] ReadArray(PhysicalMemory physicalMemory, ulong address, int size)
{
if (address == MemoryManager.PteUnmapped || size == 0)
if (address == MemoryManager.PteUnmapped || size == 0 || physicalMemory == null)
{
return Array.Empty<byte>();
return [];
}
return memoryManager.Physical.GetSpan(address, size).ToArray();
return physicalMemory.GetSpan(address, size).ToArray();
}
/// <summary>
@@ -822,16 +826,20 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <summary>
/// Creates shader translation options with the requested graphics API and flags.
/// The shader language is choosen based on the current configuration and graphics API.
/// The shader language is chosen based on the current configuration and graphics API.
/// </summary>
/// <param name="api">Target graphics API</param>
/// <param name="flags">Translation flags</param>
/// <returns>Translation options</returns>
private static TranslationOptions CreateTranslationOptions(TargetApi api, TranslationFlags flags)
{
TargetLanguage lang = GraphicsConfig.EnableSpirvCompilationOnVulkan && api == TargetApi.Vulkan
? TargetLanguage.Spirv
: TargetLanguage.Glsl;
TargetLanguage lang = api switch
{
TargetApi.OpenGL => TargetLanguage.Glsl,
TargetApi.Vulkan => GraphicsConfig.EnableSpirvCompilationOnVulkan ? TargetLanguage.Spirv : TargetLanguage.Glsl,
TargetApi.Metal => TargetLanguage.Msl,
_ => throw new NotImplementedException()
};
return new TranslationOptions(lang, api, flags);
}

View File

@@ -22,6 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
ResourceStages.Geometry;
private readonly GpuContext _context;
private readonly ComputeSize _computeLocalSize;
private int _fragmentOutputMap;
@@ -39,9 +40,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <param name="context">GPU context that owns the shaders that will be added to the builder</param>
/// <param name="tfEnabled">Indicates if the graphics shader is used with transform feedback enabled</param>
/// <param name="vertexAsCompute">Indicates that the vertex shader will be emulated on a compute shader</param>
public ShaderInfoBuilder(GpuContext context, bool tfEnabled, bool vertexAsCompute = false)
/// <param name="computeLocalSize">Indicates the local thread size for a compute shader</param>
public ShaderInfoBuilder(GpuContext context, bool tfEnabled, bool vertexAsCompute = false, ComputeSize computeLocalSize = default)
{
_context = context;
_computeLocalSize = computeLocalSize;
_fragmentOutputMap = -1;
@@ -95,7 +98,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
private void PopulateDescriptorAndUsages(ResourceStages stages, ResourceType type, int setIndex, int start, int count, bool write = false)
{
AddDescriptor(stages, type, setIndex, start, count);
AddUsage(stages, type, setIndex, start, count, write);
// AddUsage(stages, type, setIndex, start, count, write);
}
/// <summary>
@@ -159,6 +162,25 @@ namespace Ryujinx.Graphics.Gpu.Shader
AddUsage(info.Images, stages, isImage: true);
}
public void AddStageInfoVac(ShaderProgramInfo info)
{
ResourceStages stages = info.Stage switch
{
ShaderStage.Compute => ResourceStages.Compute,
ShaderStage.Vertex => ResourceStages.Vertex,
ShaderStage.TessellationControl => ResourceStages.TessellationControl,
ShaderStage.TessellationEvaluation => ResourceStages.TessellationEvaluation,
ShaderStage.Geometry => ResourceStages.Geometry,
ShaderStage.Fragment => ResourceStages.Fragment,
_ => ResourceStages.None,
};
AddUsage(info.CBuffers, stages, isStorage: false);
AddUsage(info.SBuffers, stages, isStorage: true);
AddUsage(info.Textures, stages, isImage: false);
AddUsage(info.Images, stages, isImage: true);
}
/// <summary>
/// Adds a resource descriptor to the list of descriptors.
/// </summary>
@@ -361,14 +383,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
ResourceLayout resourceLayout = new(descriptors.AsReadOnly(), usages.AsReadOnly());
if (pipeline.HasValue)
{
return new ShaderInfo(_fragmentOutputMap, resourceLayout, pipeline.Value, fromCache);
}
else
{
return new ShaderInfo(_fragmentOutputMap, resourceLayout, fromCache);
}
return new ShaderInfo(_fragmentOutputMap, resourceLayout, _computeLocalSize, pipeline, fromCache);
}
/// <summary>
@@ -378,14 +393,16 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <param name="programs">Shaders from the disk cache</param>
/// <param name="pipeline">Optional pipeline for background compilation</param>
/// <param name="tfEnabled">Indicates if the graphics shader is used with transform feedback enabled</param>
/// <param name="computeLocalSize">Compute local thread size</param>
/// <returns>Shader information</returns>
public static ShaderInfo BuildForCache(
GpuContext context,
IEnumerable<CachedShaderStage> programs,
ProgramPipelineState? pipeline,
bool tfEnabled)
bool tfEnabled,
ComputeSize computeLocalSize)
{
ShaderInfoBuilder builder = new(context, tfEnabled);
ShaderInfoBuilder builder = new(context, tfEnabled, computeLocalSize: computeLocalSize);
foreach (CachedShaderStage program in programs)
{
@@ -403,11 +420,12 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// </summary>
/// <param name="context">GPU context that owns the shader</param>
/// <param name="info">Compute shader information</param>
/// <param name="computeLocalSize">Compute local thread size</param>
/// <param name="fromCache">True if the compute shader comes from a disk cache, false otherwise</param>
/// <returns>Shader information</returns>
public static ShaderInfo BuildForCompute(GpuContext context, ShaderProgramInfo info, bool fromCache = false)
public static ShaderInfo BuildForCompute(GpuContext context, ShaderProgramInfo info, ComputeSize computeLocalSize, bool fromCache = false)
{
ShaderInfoBuilder builder = new(context, tfEnabled: false, vertexAsCompute: false);
ShaderInfoBuilder builder = new(context, tfEnabled: false, vertexAsCompute: false, computeLocalSize: computeLocalSize);
builder.AddStageInfo(info);
@@ -422,10 +440,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <param name="tfEnabled">Indicates if the graphics shader is used with transform feedback enabled</param>
/// <param name="fromCache">True if the compute shader comes from a disk cache, false otherwise</param>
/// <returns>Shader information</returns>
public static ShaderInfo BuildForVertexAsCompute(GpuContext context, ShaderProgramInfo info, bool tfEnabled, bool fromCache = false)
public static ShaderInfo BuildForVertexAsCompute(GpuContext context, ShaderProgramInfo info, ShaderProgramInfo info2, bool tfEnabled, bool fromCache = false)
{
ShaderInfoBuilder builder = new(context, tfEnabled, vertexAsCompute: true);
ShaderInfoBuilder builder = new(context, tfEnabled, vertexAsCompute: true, computeLocalSize: ComputeSize.VtgAsCompute);
builder.AddStageInfoVac(info2);
builder.AddStageInfo(info, vertexAsCompute: true);
return builder.Build(null, fromCache);

View File

@@ -696,7 +696,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
{
ref BufferBounds bounds = ref channel.BufferManager.GetUniformBufferBounds(isCompute, stageIndex, textureBufferIndex);
cachedTextureBuffer = MemoryMarshal.Cast<byte, int>(channel.MemoryManager.Physical.GetSpan(bounds.Range));
cachedTextureBuffer = MemoryMarshal.Cast<byte, int>(bounds.Physical.GetSpan(bounds.Range));
cachedTextureBufferIndex = textureBufferIndex;
if (samplerBufferIndex == textureBufferIndex)
@@ -710,7 +710,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
{
ref BufferBounds bounds = ref channel.BufferManager.GetUniformBufferBounds(isCompute, stageIndex, samplerBufferIndex);
cachedSamplerBuffer = MemoryMarshal.Cast<byte, int>(channel.MemoryManager.Physical.GetSpan(bounds.Range));
cachedSamplerBuffer = MemoryMarshal.Cast<byte, int>(bounds.Physical.GetSpan(bounds.Range));
cachedSamplerBufferIndex = samplerBufferIndex;
}

View File

@@ -9,11 +9,18 @@ using System.Threading;
namespace Ryujinx.Graphics.Gpu
{
using Texture = Image.Texture;
public record TextureData(int Width, int Height, byte[] Data);
/// <summary>
/// GPU image presentation window.
/// </summary>
public class Window
{
private const int CaptureTextureWidth = 1280;
private const int CaptureTextureHeight = 720;
private readonly GpuContext _context;
/// <summary>
@@ -85,7 +92,21 @@ namespace Ryujinx.Graphics.Gpu
}
}
private class PresentedTexture
{
public readonly Texture Texture;
public readonly ImageCrop Crop;
public PresentedTexture(Texture texture, ImageCrop crop)
{
Texture = texture;
Crop = crop;
}
}
private readonly ConcurrentQueue<PresentationTexture> _frameQueue;
private PresentedTexture _lastPresentedTexture;
private ITexture _captureTexture;
private int _framesAvailable;
@@ -188,6 +209,51 @@ namespace Ryujinx.Graphics.Gpu
return true;
}
public TextureData GetLastPresentedData()
{
PresentedTexture pt = Volatile.Read(ref _lastPresentedTexture);
if (pt != null)
{
byte[] inputData = CaptureLastFrame(pt.Texture.HostTexture, pt.Crop);
int size = SizeCalculator.GetBlockLinearTextureSize(
CaptureTextureWidth,
CaptureTextureHeight,
1,
1,
1,
1,
1,
4,
16,
1,
1).TotalSize;
byte[] data = new byte[size];
LayoutConverter.ConvertLinearToBlockLinear(data, CaptureTextureWidth, CaptureTextureHeight, CaptureTextureWidth * 4, 4, 16, inputData);
return new TextureData(CaptureTextureWidth, CaptureTextureHeight, data);
}
return new TextureData(0, 0, Array.Empty<byte>());
}
public TextureData GetLastPresentedDataLinear()
{
PresentedTexture pt = Volatile.Read(ref _lastPresentedTexture);
if (pt != null)
{
byte[] inputData = CaptureLastFrame(pt.Texture.HostTexture, new ImageCrop());
return new TextureData(pt.Texture.Info.Width, pt.Texture.Info.Height, inputData);
}
return new TextureData(0, 0, Array.Empty<byte>());
}
/// <summary>
/// Presents a texture on the queue.
/// If the queue is empty, then no texture is presented.
@@ -205,6 +271,10 @@ namespace Ryujinx.Graphics.Gpu
pt.Cache.Tick();
EnsureCaptureTexture();
Volatile.Write(ref _lastPresentedTexture, new PresentedTexture(texture, pt.Crop));
texture.SynchronizeMemory();
ImageCrop crop = new(
@@ -244,6 +314,96 @@ namespace Ryujinx.Graphics.Gpu
}
}
private void EnsureCaptureTexture()
{
if (_captureTexture == null)
{
_captureTexture = _context.Renderer.CreateTexture(new TextureCreateInfo(
1280,
720,
1,
1,
1,
1,
1,
4,
Format.R8G8B8A8Unorm,
DepthStencilMode.Depth,
Target.Texture2D,
SwizzleComponent.Red,
SwizzleComponent.Green,
SwizzleComponent.Blue,
SwizzleComponent.Alpha));
}
}
private byte[] CaptureLastFrame(ITexture lastFrame, ImageCrop crop)
{
int cropLeft, cropRight, cropTop, cropBottom;
if (crop.Left == 0 && crop.Right == 0)
{
cropLeft = 0;
cropRight = lastFrame.Width;
}
else
{
cropLeft = crop.Left;
cropRight = crop.Right;
}
if (crop.Top == 0 && crop.Bottom == 0)
{
cropTop = 0;
cropBottom = lastFrame.Height;
}
else
{
cropTop = crop.Top;
cropBottom = crop.Bottom;
}
int x1, y1, x2, y2;
if (crop.FlipX)
{
x1 = cropRight;
x2 = cropLeft;
}
else
{
x1 = cropLeft;
x2 = cropRight;
}
if (crop.FlipY)
{
y1 = cropBottom;
y2 = cropTop;
}
else
{
y1 = cropTop;
y2 = cropBottom;
}
Extents2D srcRegion = new(x1, y1, x2, y2);
Extents2D dstRegion = new(0, 0, CaptureTextureWidth, CaptureTextureHeight);
byte[] outputData = null;
_context.Renderer.BackgroundContextAction(() =>
{
lastFrame.CopyTo(_captureTexture, srcRegion, dstRegion, true);
using var data = _captureTexture.GetData();
outputData = data.Get().ToArray();
});
return outputData;
}
/// <summary>
/// Indicate that a frame on the queue is ready to be acquired.
/// </summary>

View File

@@ -2,7 +2,6 @@
<PropertyGroup>
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
<Configurations>Debug;Release;DebugAOT;ReleaseAOT</Configurations>
</PropertyGroup>
<ItemGroup>

View File

@@ -0,0 +1,146 @@
using System;
using System.Diagnostics;
using System.Runtime.Versioning;
using System.Threading;
namespace Ryujinx.Graphics.Metal
{
interface IAuto
{
bool HasCommandBufferDependency(CommandBufferScoped cbs);
void IncrementReferenceCount();
void DecrementReferenceCount(int cbIndex);
void DecrementReferenceCount();
}
interface IAutoPrivate : IAuto
{
void AddCommandBufferDependencies(CommandBufferScoped cbs);
}
[SupportedOSPlatform("macos")]
class Auto<T> : IAutoPrivate, IDisposable where T : IDisposable
{
private int _referenceCount;
private T _value;
private readonly BitMap _cbOwnership;
private readonly MultiFenceHolder _waitable;
private bool _disposed;
private bool _destroyed;
public Auto(T value)
{
_referenceCount = 1;
_value = value;
_cbOwnership = new BitMap(CommandBufferPool.MaxCommandBuffers);
}
public Auto(T value, MultiFenceHolder waitable) : this(value)
{
_waitable = waitable;
}
public T Get(CommandBufferScoped cbs, int offset, int size, bool write = false)
{
_waitable?.AddBufferUse(cbs.CommandBufferIndex, offset, size, write);
return Get(cbs);
}
public T GetUnsafe()
{
return _value;
}
public T Get(CommandBufferScoped cbs)
{
if (!_destroyed)
{
AddCommandBufferDependencies(cbs);
}
return _value;
}
public bool HasCommandBufferDependency(CommandBufferScoped cbs)
{
return _cbOwnership.IsSet(cbs.CommandBufferIndex);
}
public bool HasRentedCommandBufferDependency(CommandBufferPool cbp)
{
return _cbOwnership.AnySet();
}
public void AddCommandBufferDependencies(CommandBufferScoped cbs)
{
// We don't want to add a reference to this object to the command buffer
// more than once, so if we detect that the command buffer already has ownership
// of this object, then we can just return without doing anything else.
if (_cbOwnership.Set(cbs.CommandBufferIndex))
{
if (_waitable != null)
{
cbs.AddWaitable(_waitable);
}
cbs.AddDependant(this);
}
}
public bool TryIncrementReferenceCount()
{
int lastValue;
do
{
lastValue = _referenceCount;
if (lastValue == 0)
{
return false;
}
}
while (Interlocked.CompareExchange(ref _referenceCount, lastValue + 1, lastValue) != lastValue);
return true;
}
public void IncrementReferenceCount()
{
if (Interlocked.Increment(ref _referenceCount) == 1)
{
Interlocked.Decrement(ref _referenceCount);
throw new InvalidOperationException("Attempted to increment the reference count of an object that was already destroyed.");
}
}
public void DecrementReferenceCount(int cbIndex)
{
_cbOwnership.Clear(cbIndex);
DecrementReferenceCount();
}
public void DecrementReferenceCount()
{
if (Interlocked.Decrement(ref _referenceCount) == 0)
{
_value.Dispose();
_value = default;
_destroyed = true;
}
Debug.Assert(_referenceCount >= 0);
}
public void Dispose()
{
if (!_disposed)
{
DecrementReferenceCount();
_disposed = true;
}
}
}
}

View File

@@ -0,0 +1,107 @@
using SharpMetal.Metal;
using System;
using System.Collections.Generic;
using System.Runtime.Versioning;
using System.Threading;
namespace Ryujinx.Graphics.Metal
{
[SupportedOSPlatform("macos")]
class BackgroundResource : IDisposable
{
private readonly MetalRenderer _renderer;
private CommandBufferPool _pool;
private PersistentFlushBuffer _flushBuffer;
public BackgroundResource(MetalRenderer renderer)
{
_renderer = renderer;
}
public CommandBufferPool GetPool()
{
if (_pool == null)
{
MTLCommandQueue queue = _renderer.BackgroundQueue;
_pool = new CommandBufferPool(queue, true);
_pool.Initialize(null); // TODO: Proper encoder factory for background render/compute
}
return _pool;
}
public PersistentFlushBuffer GetFlushBuffer()
{
_flushBuffer ??= new PersistentFlushBuffer(_renderer);
return _flushBuffer;
}
public void Dispose()
{
_pool?.Dispose();
_flushBuffer?.Dispose();
}
}
[SupportedOSPlatform("macos")]
class BackgroundResources : IDisposable
{
private readonly MetalRenderer _renderer;
private readonly Dictionary<Thread, BackgroundResource> _resources;
public BackgroundResources(MetalRenderer renderer)
{
_renderer = renderer;
_resources = new Dictionary<Thread, BackgroundResource>();
}
private void Cleanup()
{
lock (_resources)
{
foreach (KeyValuePair<Thread, BackgroundResource> tuple in _resources)
{
if (!tuple.Key.IsAlive)
{
tuple.Value.Dispose();
_resources.Remove(tuple.Key);
}
}
}
}
public BackgroundResource Get()
{
Thread thread = Thread.CurrentThread;
lock (_resources)
{
if (!_resources.TryGetValue(thread, out BackgroundResource resource))
{
Cleanup();
resource = new BackgroundResource(_renderer);
_resources[thread] = resource;
}
return resource;
}
}
public void Dispose()
{
lock (_resources)
{
foreach (BackgroundResource resource in _resources.Values)
{
resource.Dispose();
}
}
}
}
}

View File

@@ -0,0 +1,157 @@
namespace Ryujinx.Graphics.Metal
{
readonly struct BitMap
{
public const int IntSize = 64;
private const int IntShift = 6;
private const int IntMask = IntSize - 1;
private readonly long[] _masks;
public BitMap(int count)
{
_masks = new long[(count + IntMask) / IntSize];
}
public bool AnySet()
{
for (int i = 0; i < _masks.Length; i++)
{
if (_masks[i] != 0)
{
return true;
}
}
return false;
}
public bool IsSet(int bit)
{
int wordIndex = bit >> IntShift;
int wordBit = bit & IntMask;
long wordMask = 1L << wordBit;
return (_masks[wordIndex] & wordMask) != 0;
}
public bool IsSet(int start, int end)
{
if (start == end)
{
return IsSet(start);
}
int startIndex = start >> IntShift;
int startBit = start & IntMask;
long startMask = -1L << startBit;
int endIndex = end >> IntShift;
int endBit = end & IntMask;
long endMask = (long)(ulong.MaxValue >> (IntMask - endBit));
if (startIndex == endIndex)
{
return (_masks[startIndex] & startMask & endMask) != 0;
}
if ((_masks[startIndex] & startMask) != 0)
{
return true;
}
for (int i = startIndex + 1; i < endIndex; i++)
{
if (_masks[i] != 0)
{
return true;
}
}
if ((_masks[endIndex] & endMask) != 0)
{
return true;
}
return false;
}
public bool Set(int bit)
{
int wordIndex = bit >> IntShift;
int wordBit = bit & IntMask;
long wordMask = 1L << wordBit;
if ((_masks[wordIndex] & wordMask) != 0)
{
return false;
}
_masks[wordIndex] |= wordMask;
return true;
}
public void SetRange(int start, int end)
{
if (start == end)
{
Set(start);
return;
}
int startIndex = start >> IntShift;
int startBit = start & IntMask;
long startMask = -1L << startBit;
int endIndex = end >> IntShift;
int endBit = end & IntMask;
long endMask = (long)(ulong.MaxValue >> (IntMask - endBit));
if (startIndex == endIndex)
{
_masks[startIndex] |= startMask & endMask;
}
else
{
_masks[startIndex] |= startMask;
for (int i = startIndex + 1; i < endIndex; i++)
{
_masks[i] |= -1;
}
_masks[endIndex] |= endMask;
}
}
public void Clear(int bit)
{
int wordIndex = bit >> IntShift;
int wordBit = bit & IntMask;
long wordMask = 1L << wordBit;
_masks[wordIndex] &= ~wordMask;
}
public void Clear()
{
for (int i = 0; i < _masks.Length; i++)
{
_masks[i] = 0;
}
}
public void ClearInt(int start, int end)
{
for (int i = start; i <= end; i++)
{
_masks[i] = 0;
}
}
}
}

View File

@@ -0,0 +1,385 @@
using Ryujinx.Graphics.GAL;
using SharpMetal.Metal;
using System;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Threading;
namespace Ryujinx.Graphics.Metal
{
[SupportedOSPlatform("macos")]
class BufferHolder : IDisposable
{
private CacheByRange<BufferHolder> _cachedConvertedBuffers;
public int Size { get; }
private readonly IntPtr _map;
private readonly MetalRenderer _renderer;
private readonly Pipeline _pipeline;
private readonly MultiFenceHolder _waitable;
private readonly Auto<DisposableBuffer> _buffer;
private readonly ReaderWriterLockSlim _flushLock;
private FenceHolder _flushFence;
private int _flushWaiting;
public BufferHolder(MetalRenderer renderer, Pipeline pipeline, MTLBuffer buffer, int size)
{
_renderer = renderer;
_pipeline = pipeline;
_map = buffer.Contents;
_waitable = new MultiFenceHolder(size);
_buffer = new Auto<DisposableBuffer>(new(buffer), _waitable);
_flushLock = new ReaderWriterLockSlim();
Size = size;
}
public Auto<DisposableBuffer> GetBuffer()
{
return _buffer;
}
public Auto<DisposableBuffer> GetBuffer(bool isWrite)
{
if (isWrite)
{
SignalWrite(0, Size);
}
return _buffer;
}
public Auto<DisposableBuffer> GetBuffer(int offset, int size, bool isWrite)
{
if (isWrite)
{
SignalWrite(offset, size);
}
return _buffer;
}
public void SignalWrite(int offset, int size)
{
if (offset == 0 && size == Size)
{
_cachedConvertedBuffers.Clear();
}
else
{
_cachedConvertedBuffers.ClearRange(offset, size);
}
}
private void ClearFlushFence()
{
// Assumes _flushLock is held as writer.
if (_flushFence != null)
{
if (_flushWaiting == 0)
{
_flushFence.Put();
}
_flushFence = null;
}
}
private void WaitForFlushFence()
{
if (_flushFence == null)
{
return;
}
// If storage has changed, make sure the fence has been reached so that the data is in place.
_flushLock.ExitReadLock();
_flushLock.EnterWriteLock();
if (_flushFence != null)
{
FenceHolder fence = _flushFence;
Interlocked.Increment(ref _flushWaiting);
// Don't wait in the lock.
_flushLock.ExitWriteLock();
fence.Wait();
_flushLock.EnterWriteLock();
if (Interlocked.Decrement(ref _flushWaiting) == 0)
{
fence.Put();
}
_flushFence = null;
}
// Assumes the _flushLock is held as reader, returns in same state.
_flushLock.ExitWriteLock();
_flushLock.EnterReadLock();
}
public PinnedSpan<byte> GetData(int offset, int size)
{
_flushLock.EnterReadLock();
WaitForFlushFence();
Span<byte> result;
if (_map != IntPtr.Zero)
{
result = GetDataStorage(offset, size);
// Need to be careful here, the buffer can't be unmapped while the data is being used.
_buffer.IncrementReferenceCount();
_flushLock.ExitReadLock();
return PinnedSpan<byte>.UnsafeFromSpan(result, _buffer.DecrementReferenceCount);
}
throw new InvalidOperationException("The buffer is not mapped");
}
public unsafe Span<byte> GetDataStorage(int offset, int size)
{
int mappingSize = Math.Min(size, Size - offset);
if (_map != IntPtr.Zero)
{
return new Span<byte>((void*)(_map + offset), mappingSize);
}
throw new InvalidOperationException("The buffer is not mapped.");
}
public unsafe void SetData(int offset, ReadOnlySpan<byte> data, CommandBufferScoped? cbs = null, bool allowCbsWait = true)
{
int dataSize = Math.Min(data.Length, Size - offset);
if (dataSize == 0)
{
return;
}
if (_map != IntPtr.Zero)
{
// If persistently mapped, set the data directly if the buffer is not currently in use.
bool isRented = _buffer.HasRentedCommandBufferDependency(_renderer.CommandBufferPool);
// If the buffer is rented, take a little more time and check if the use overlaps this handle.
bool needsFlush = isRented && _waitable.IsBufferRangeInUse(offset, dataSize, false);
if (!needsFlush)
{
WaitForFences(offset, dataSize);
data[..dataSize].CopyTo(new Span<byte>((void*)(_map + offset), dataSize));
SignalWrite(offset, dataSize);
return;
}
}
if (cbs != null &&
cbs.Value.Encoders.CurrentEncoderType == EncoderType.Render &&
!(_buffer.HasCommandBufferDependency(cbs.Value) &&
_waitable.IsBufferRangeInUse(cbs.Value.CommandBufferIndex, offset, dataSize)))
{
// If the buffer hasn't been used on the command buffer yet, try to preload the data.
// This avoids ending and beginning render passes on each buffer data upload.
cbs = _pipeline.GetPreloadCommandBuffer();
}
if (allowCbsWait)
{
_renderer.BufferManager.StagingBuffer.PushData(_renderer.CommandBufferPool, cbs, this, offset, data);
}
else
{
bool rentCbs = cbs == null;
if (rentCbs)
{
cbs = _renderer.CommandBufferPool.Rent();
}
if (!_renderer.BufferManager.StagingBuffer.TryPushData(cbs.Value, this, offset, data))
{
// Need to do a slow upload.
BufferHolder srcHolder = _renderer.BufferManager.Create(dataSize);
srcHolder.SetDataUnchecked(0, data);
Auto<DisposableBuffer> srcBuffer = srcHolder.GetBuffer();
Auto<DisposableBuffer> dstBuffer = this.GetBuffer(true);
Copy(cbs.Value, srcBuffer, dstBuffer, 0, offset, dataSize);
srcHolder.Dispose();
}
if (rentCbs)
{
cbs.Value.Dispose();
}
}
}
public unsafe void SetDataUnchecked(int offset, ReadOnlySpan<byte> data)
{
int dataSize = Math.Min(data.Length, Size - offset);
if (dataSize == 0)
{
return;
}
if (_map != IntPtr.Zero)
{
data[..dataSize].CopyTo(new Span<byte>((void*)(_map + offset), dataSize));
}
}
public void SetDataUnchecked<T>(int offset, ReadOnlySpan<T> data) where T : unmanaged
{
SetDataUnchecked(offset, MemoryMarshal.AsBytes(data));
}
public static void Copy(
CommandBufferScoped cbs,
Auto<DisposableBuffer> src,
Auto<DisposableBuffer> dst,
int srcOffset,
int dstOffset,
int size,
bool registerSrcUsage = true)
{
MTLBuffer srcBuffer = registerSrcUsage ? src.Get(cbs, srcOffset, size).Value : src.GetUnsafe().Value;
MTLBuffer dstbuffer = dst.Get(cbs, dstOffset, size, true).Value;
cbs.Encoders.EnsureBlitEncoder().CopyFromBuffer(
srcBuffer,
(ulong)srcOffset,
dstbuffer,
(ulong)dstOffset,
(ulong)size);
}
public void WaitForFences()
{
_waitable.WaitForFences();
}
public void WaitForFences(int offset, int size)
{
_waitable.WaitForFences(offset, size);
}
private bool BoundToRange(int offset, ref int size)
{
if (offset >= Size)
{
return false;
}
size = Math.Min(Size - offset, size);
return true;
}
public Auto<DisposableBuffer> GetBufferI8ToI16(CommandBufferScoped cbs, int offset, int size)
{
if (!BoundToRange(offset, ref size))
{
return null;
}
I8ToI16CacheKey key = new(_renderer);
if (!_cachedConvertedBuffers.TryGetValue(offset, size, key, out BufferHolder holder))
{
holder = _renderer.BufferManager.Create((size * 2 + 3) & ~3);
_renderer.HelperShader.ConvertI8ToI16(cbs, this, holder, offset, size);
key.SetBuffer(holder.GetBuffer());
_cachedConvertedBuffers.Add(offset, size, key, holder);
}
return holder.GetBuffer();
}
public Auto<DisposableBuffer> GetBufferTopologyConversion(CommandBufferScoped cbs, int offset, int size, IndexBufferPattern pattern, int indexSize)
{
if (!BoundToRange(offset, ref size))
{
return null;
}
TopologyConversionCacheKey key = new(_renderer, pattern, indexSize);
if (!_cachedConvertedBuffers.TryGetValue(offset, size, key, out BufferHolder holder))
{
// The destination index size is always I32.
int indexCount = size / indexSize;
int convertedCount = pattern.GetConvertedCount(indexCount);
holder = _renderer.BufferManager.Create(convertedCount * 4);
_renderer.HelperShader.ConvertIndexBuffer(cbs, this, holder, pattern, indexSize, offset, indexCount);
key.SetBuffer(holder.GetBuffer());
_cachedConvertedBuffers.Add(offset, size, key, holder);
}
return holder.GetBuffer();
}
public bool TryGetCachedConvertedBuffer(int offset, int size, ICacheKey key, out BufferHolder holder)
{
return _cachedConvertedBuffers.TryGetValue(offset, size, key, out holder);
}
public void AddCachedConvertedBuffer(int offset, int size, ICacheKey key, BufferHolder holder)
{
_cachedConvertedBuffers.Add(offset, size, key, holder);
}
public void AddCachedConvertedBufferDependency(int offset, int size, ICacheKey key, Dependency dependency)
{
_cachedConvertedBuffers.AddDependency(offset, size, key, dependency);
}
public void RemoveCachedConvertedBuffer(int offset, int size, ICacheKey key)
{
_cachedConvertedBuffers.Remove(offset, size, key);
}
public void Dispose()
{
_pipeline.FlushCommandsIfWeightExceeding(_buffer, (ulong)Size);
_buffer.Dispose();
_cachedConvertedBuffers.Dispose();
_flushLock.EnterWriteLock();
ClearFlushFence();
_flushLock.ExitWriteLock();
}
}
}

View File

@@ -0,0 +1,237 @@
using Ryujinx.Common.Logging;
using Ryujinx.Graphics.GAL;
using SharpMetal.Metal;
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
namespace Ryujinx.Graphics.Metal
{
[SupportedOSPlatform("macos")]
readonly struct ScopedTemporaryBuffer : IDisposable
{
private readonly BufferManager _bufferManager;
private readonly bool _isReserved;
public readonly BufferRange Range;
public readonly BufferHolder Holder;
public BufferHandle Handle => Range.Handle;
public int Offset => Range.Offset;
public ScopedTemporaryBuffer(BufferManager bufferManager, BufferHolder holder, BufferHandle handle, int offset, int size, bool isReserved)
{
_bufferManager = bufferManager;
Range = new BufferRange(handle, offset, size);
Holder = holder;
_isReserved = isReserved;
}
public void Dispose()
{
if (!_isReserved)
{
_bufferManager.Delete(Range.Handle);
}
}
}
[SupportedOSPlatform("macos")]
class BufferManager : IDisposable
{
private readonly IdList<BufferHolder> _buffers;
private readonly MTLDevice _device;
private readonly MetalRenderer _renderer;
private readonly Pipeline _pipeline;
public int BufferCount { get; private set; }
public StagingBuffer StagingBuffer { get; }
public BufferManager(MTLDevice device, MetalRenderer renderer, Pipeline pipeline)
{
_device = device;
_renderer = renderer;
_pipeline = pipeline;
_buffers = new IdList<BufferHolder>();
StagingBuffer = new StagingBuffer(_renderer, this);
}
public BufferHandle Create(nint pointer, int size)
{
// TODO: This is the wrong Metal method, we need no-copy which SharpMetal isn't giving us.
MTLBuffer buffer = _device.NewBuffer(pointer, (ulong)size, MTLResourceOptions.ResourceStorageModeShared);
if (buffer == IntPtr.Zero)
{
Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create buffer with size 0x{size:X}, and pointer 0x{pointer:X}.");
return BufferHandle.Null;
}
BufferHolder holder = new(_renderer, _pipeline, buffer, size);
BufferCount++;
ulong handle64 = (uint)_buffers.Add(holder);
return Unsafe.As<ulong, BufferHandle>(ref handle64);
}
public BufferHandle CreateWithHandle(int size)
{
return CreateWithHandle(size, out _);
}
public BufferHandle CreateWithHandle(int size, out BufferHolder holder)
{
holder = Create(size);
if (holder == null)
{
return BufferHandle.Null;
}
BufferCount++;
ulong handle64 = (uint)_buffers.Add(holder);
return Unsafe.As<ulong, BufferHandle>(ref handle64);
}
public ScopedTemporaryBuffer ReserveOrCreate(CommandBufferScoped cbs, int size)
{
StagingBufferReserved? result = StagingBuffer.TryReserveData(cbs, size);
if (result.HasValue)
{
return new ScopedTemporaryBuffer(this, result.Value.Buffer, StagingBuffer.Handle, result.Value.Offset, result.Value.Size, true);
}
else
{
// Create a temporary buffer.
BufferHandle handle = CreateWithHandle(size, out BufferHolder holder);
return new ScopedTemporaryBuffer(this, holder, handle, 0, size, false);
}
}
public BufferHolder Create(int size)
{
MTLBuffer buffer = _device.NewBuffer((ulong)size, MTLResourceOptions.ResourceStorageModeShared);
if (buffer != IntPtr.Zero)
{
return new BufferHolder(_renderer, _pipeline, buffer, size);
}
Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create buffer with size 0x{size:X}.");
return null;
}
public Auto<DisposableBuffer> GetBuffer(BufferHandle handle, bool isWrite, out int size)
{
if (TryGetBuffer(handle, out BufferHolder holder))
{
size = holder.Size;
return holder.GetBuffer(isWrite);
}
size = 0;
return null;
}
public Auto<DisposableBuffer> GetBuffer(BufferHandle handle, int offset, int size, bool isWrite)
{
if (TryGetBuffer(handle, out BufferHolder holder))
{
return holder.GetBuffer(offset, size, isWrite);
}
return null;
}
public Auto<DisposableBuffer> GetBuffer(BufferHandle handle, bool isWrite)
{
if (TryGetBuffer(handle, out BufferHolder holder))
{
return holder.GetBuffer(isWrite);
}
return null;
}
public Auto<DisposableBuffer> GetBufferI8ToI16(CommandBufferScoped cbs, BufferHandle handle, int offset, int size)
{
if (TryGetBuffer(handle, out BufferHolder holder))
{
return holder.GetBufferI8ToI16(cbs, offset, size);
}
return null;
}
public Auto<DisposableBuffer> GetBufferTopologyConversion(CommandBufferScoped cbs, BufferHandle handle, int offset, int size, IndexBufferPattern pattern, int indexSize)
{
if (TryGetBuffer(handle, out BufferHolder holder))
{
return holder.GetBufferTopologyConversion(cbs, offset, size, pattern, indexSize);
}
return null;
}
public PinnedSpan<byte> GetData(BufferHandle handle, int offset, int size)
{
if (TryGetBuffer(handle, out BufferHolder holder))
{
return holder.GetData(offset, size);
}
return new PinnedSpan<byte>();
}
public void SetData<T>(BufferHandle handle, int offset, ReadOnlySpan<T> data) where T : unmanaged
{
SetData(handle, offset, MemoryMarshal.Cast<T, byte>(data), null);
}
public void SetData(BufferHandle handle, int offset, ReadOnlySpan<byte> data, CommandBufferScoped? cbs)
{
if (TryGetBuffer(handle, out BufferHolder holder))
{
holder.SetData(offset, data, cbs);
}
}
public void Delete(BufferHandle handle)
{
if (TryGetBuffer(handle, out BufferHolder holder))
{
holder.Dispose();
_buffers.Remove((int)Unsafe.As<BufferHandle, ulong>(ref handle));
}
}
private bool TryGetBuffer(BufferHandle handle, out BufferHolder holder)
{
return _buffers.TryGetValue((int)Unsafe.As<BufferHandle, ulong>(ref handle), out holder);
}
public void Dispose()
{
StagingBuffer.Dispose();
foreach (BufferHolder buffer in _buffers)
{
buffer.Dispose();
}
}
}
}

View File

@@ -0,0 +1,85 @@
using System.Runtime.Versioning;
namespace Ryujinx.Graphics.Metal
{
[SupportedOSPlatform("macos")]
internal class BufferUsageBitmap
{
private readonly BitMap _bitmap;
private readonly int _size;
private readonly int _granularity;
private readonly int _bits;
private readonly int _writeBitOffset;
private readonly int _intsPerCb;
private readonly int _bitsPerCb;
public BufferUsageBitmap(int size, int granularity)
{
_size = size;
_granularity = granularity;
// There are two sets of bits - one for read tracking, and the other for write.
int bits = (size + (granularity - 1)) / granularity;
_writeBitOffset = bits;
_bits = bits << 1;
_intsPerCb = (_bits + (BitMap.IntSize - 1)) / BitMap.IntSize;
_bitsPerCb = _intsPerCb * BitMap.IntSize;
_bitmap = new BitMap(_bitsPerCb * CommandBufferPool.MaxCommandBuffers);
}
public void Add(int cbIndex, int offset, int size, bool write)
{
if (size == 0)
{
return;
}
// Some usages can be out of bounds (vertex buffer on amd), so bound if necessary.
if (offset + size > _size)
{
size = _size - offset;
}
int cbBase = cbIndex * _bitsPerCb + (write ? _writeBitOffset : 0);
int start = cbBase + offset / _granularity;
int end = cbBase + (offset + size - 1) / _granularity;
_bitmap.SetRange(start, end);
}
public bool OverlapsWith(int cbIndex, int offset, int size, bool write = false)
{
if (size == 0)
{
return false;
}
int cbBase = cbIndex * _bitsPerCb + (write ? _writeBitOffset : 0);
int start = cbBase + offset / _granularity;
int end = cbBase + (offset + size - 1) / _granularity;
return _bitmap.IsSet(start, end);
}
public bool OverlapsWith(int offset, int size, bool write)
{
for (int i = 0; i < CommandBufferPool.MaxCommandBuffers; i++)
{
if (OverlapsWith(i, offset, size, write))
{
return true;
}
}
return false;
}
public void Clear(int cbIndex)
{
_bitmap.ClearInt(cbIndex * _intsPerCb, (cbIndex + 1) * _intsPerCb - 1);
}
}
}

View File

@@ -0,0 +1,294 @@
using System;
using System.Collections.Generic;
using System.Runtime.Versioning;
namespace Ryujinx.Graphics.Metal
{
interface ICacheKey : IDisposable
{
bool KeyEqual(ICacheKey other);
}
[SupportedOSPlatform("macos")]
struct I8ToI16CacheKey : ICacheKey
{
// Used to notify the pipeline that bindings have invalidated on dispose.
// private readonly MetalRenderer _renderer;
// private Auto<DisposableBuffer> _buffer;
public I8ToI16CacheKey(MetalRenderer renderer)
{
// _renderer = renderer;
// _buffer = null;
}
public readonly bool KeyEqual(ICacheKey other)
{
return other is I8ToI16CacheKey;
}
public readonly void SetBuffer(Auto<DisposableBuffer> buffer)
{
// _buffer = buffer;
}
public readonly void Dispose()
{
// TODO: Tell pipeline buffer is dirty!
// _renderer.PipelineInternal.DirtyIndexBuffer(_buffer);
}
}
[SupportedOSPlatform("macos")]
readonly struct TopologyConversionCacheKey : ICacheKey
{
private readonly IndexBufferPattern _pattern;
private readonly int _indexSize;
// Used to notify the pipeline that bindings have invalidated on dispose.
// private readonly MetalRenderer _renderer;
// private Auto<DisposableBuffer> _buffer;
public TopologyConversionCacheKey(MetalRenderer renderer, IndexBufferPattern pattern, int indexSize)
{
// _renderer = renderer;
// _buffer = null;
_pattern = pattern;
_indexSize = indexSize;
}
public readonly bool KeyEqual(ICacheKey other)
{
return other is TopologyConversionCacheKey entry &&
entry._pattern == _pattern &&
entry._indexSize == _indexSize;
}
public void SetBuffer(Auto<DisposableBuffer> buffer)
{
// _buffer = buffer;
}
public readonly void Dispose()
{
// TODO: Tell pipeline buffer is dirty!
// _renderer.PipelineInternal.DirtyVertexBuffer(_buffer);
}
}
[SupportedOSPlatform("macos")]
readonly struct Dependency
{
private readonly BufferHolder _buffer;
private readonly int _offset;
private readonly int _size;
private readonly ICacheKey _key;
public Dependency(BufferHolder buffer, int offset, int size, ICacheKey key)
{
_buffer = buffer;
_offset = offset;
_size = size;
_key = key;
}
public void RemoveFromOwner()
{
_buffer.RemoveCachedConvertedBuffer(_offset, _size, _key);
}
}
[SupportedOSPlatform("macos")]
struct CacheByRange<T> where T : IDisposable
{
private struct Entry
{
public readonly ICacheKey Key;
public readonly T Value;
public List<Dependency> DependencyList;
public Entry(ICacheKey key, T value)
{
Key = key;
Value = value;
DependencyList = null;
}
public readonly void InvalidateDependencies()
{
if (DependencyList != null)
{
foreach (Dependency dependency in DependencyList)
{
dependency.RemoveFromOwner();
}
DependencyList.Clear();
}
}
}
private Dictionary<ulong, List<Entry>> _ranges;
public void Add(int offset, int size, ICacheKey key, T value)
{
List<Entry> entries = GetEntries(offset, size);
entries.Add(new Entry(key, value));
}
public void AddDependency(int offset, int size, ICacheKey key, Dependency dependency)
{
List<Entry> entries = GetEntries(offset, size);
for (int i = 0; i < entries.Count; i++)
{
Entry entry = entries[i];
if (entry.Key.KeyEqual(key))
{
if (entry.DependencyList == null)
{
entry.DependencyList = [];
entries[i] = entry;
}
entry.DependencyList.Add(dependency);
break;
}
}
}
public void Remove(int offset, int size, ICacheKey key)
{
List<Entry> entries = GetEntries(offset, size);
for (int i = 0; i < entries.Count; i++)
{
Entry entry = entries[i];
if (entry.Key.KeyEqual(key))
{
entries.RemoveAt(i--);
DestroyEntry(entry);
}
}
if (entries.Count == 0)
{
_ranges.Remove(PackRange(offset, size));
}
}
public bool TryGetValue(int offset, int size, ICacheKey key, out T value)
{
List<Entry> entries = GetEntries(offset, size);
foreach (Entry entry in entries)
{
if (entry.Key.KeyEqual(key))
{
value = entry.Value;
return true;
}
}
value = default;
return false;
}
public void Clear()
{
if (_ranges != null)
{
foreach (List<Entry> entries in _ranges.Values)
{
foreach (Entry entry in entries)
{
DestroyEntry(entry);
}
}
_ranges.Clear();
_ranges = null;
}
}
public readonly void ClearRange(int offset, int size)
{
if (_ranges != null && _ranges.Count > 0)
{
int end = offset + size;
List<ulong> toRemove = null;
foreach (KeyValuePair<ulong, List<Entry>> range in _ranges)
{
(int rOffset, int rSize) = UnpackRange(range.Key);
int rEnd = rOffset + rSize;
if (rEnd > offset && rOffset < end)
{
List<Entry> entries = range.Value;
foreach (Entry entry in entries)
{
DestroyEntry(entry);
}
(toRemove ??= []).Add(range.Key);
}
}
if (toRemove != null)
{
foreach (ulong range in toRemove)
{
_ranges.Remove(range);
}
}
}
}
private List<Entry> GetEntries(int offset, int size)
{
_ranges ??= new Dictionary<ulong, List<Entry>>();
ulong key = PackRange(offset, size);
if (!_ranges.TryGetValue(key, out List<Entry> value))
{
value = [];
_ranges.Add(key, value);
}
return value;
}
private static void DestroyEntry(Entry entry)
{
entry.Key.Dispose();
entry.Value?.Dispose();
entry.InvalidateDependencies();
}
private static ulong PackRange(int offset, int size)
{
return (uint)offset | ((ulong)size << 32);
}
private static (int offset, int size) UnpackRange(ulong range)
{
return ((int)range, (int)(range >> 32));
}
public void Dispose()
{
Clear();
}
}
}

View File

@@ -0,0 +1,170 @@
using Ryujinx.Graphics.Metal;
using SharpMetal.Metal;
using System;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
interface IEncoderFactory
{
MTLRenderCommandEncoder CreateRenderCommandEncoder();
MTLComputeCommandEncoder CreateComputeCommandEncoder();
}
/// <summary>
/// Tracks active encoder object for a command buffer.
/// </summary>
[SupportedOSPlatform("macos")]
class CommandBufferEncoder
{
public EncoderType CurrentEncoderType { get; private set; } = EncoderType.None;
public MTLBlitCommandEncoder BlitEncoder => new(CurrentEncoder.Value);
public MTLComputeCommandEncoder ComputeEncoder => new(CurrentEncoder.Value);
public MTLRenderCommandEncoder RenderEncoder => new(CurrentEncoder.Value);
internal MTLCommandEncoder? CurrentEncoder { get; private set; }
private MTLCommandBuffer _commandBuffer;
private IEncoderFactory _encoderFactory;
public void Initialize(MTLCommandBuffer commandBuffer, IEncoderFactory encoderFactory)
{
_commandBuffer = commandBuffer;
_encoderFactory = encoderFactory;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public MTLRenderCommandEncoder EnsureRenderEncoder()
{
if (CurrentEncoderType != EncoderType.Render)
{
return BeginRenderPass();
}
return RenderEncoder;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public MTLBlitCommandEncoder EnsureBlitEncoder()
{
if (CurrentEncoderType != EncoderType.Blit)
{
return BeginBlitPass();
}
return BlitEncoder;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public MTLComputeCommandEncoder EnsureComputeEncoder()
{
if (CurrentEncoderType != EncoderType.Compute)
{
return BeginComputePass();
}
return ComputeEncoder;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryGetRenderEncoder(out MTLRenderCommandEncoder encoder)
{
if (CurrentEncoderType != EncoderType.Render)
{
encoder = default;
return false;
}
encoder = RenderEncoder;
return true;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryGetBlitEncoder(out MTLBlitCommandEncoder encoder)
{
if (CurrentEncoderType != EncoderType.Blit)
{
encoder = default;
return false;
}
encoder = BlitEncoder;
return true;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryGetComputeEncoder(out MTLComputeCommandEncoder encoder)
{
if (CurrentEncoderType != EncoderType.Compute)
{
encoder = default;
return false;
}
encoder = ComputeEncoder;
return true;
}
public void EndCurrentPass()
{
if (CurrentEncoder != null)
{
switch (CurrentEncoderType)
{
case EncoderType.Blit:
BlitEncoder.EndEncoding();
CurrentEncoder = null;
break;
case EncoderType.Compute:
ComputeEncoder.EndEncoding();
CurrentEncoder = null;
break;
case EncoderType.Render:
RenderEncoder.EndEncoding();
CurrentEncoder = null;
break;
default:
throw new InvalidOperationException();
}
CurrentEncoderType = EncoderType.None;
}
}
private MTLRenderCommandEncoder BeginRenderPass()
{
EndCurrentPass();
MTLRenderCommandEncoder renderCommandEncoder = _encoderFactory.CreateRenderCommandEncoder();
CurrentEncoder = renderCommandEncoder;
CurrentEncoderType = EncoderType.Render;
return renderCommandEncoder;
}
private MTLBlitCommandEncoder BeginBlitPass()
{
EndCurrentPass();
using MTLBlitPassDescriptor descriptor = new();
MTLBlitCommandEncoder blitCommandEncoder = _commandBuffer.BlitCommandEncoder(descriptor);
CurrentEncoder = blitCommandEncoder;
CurrentEncoderType = EncoderType.Blit;
return blitCommandEncoder;
}
private MTLComputeCommandEncoder BeginComputePass()
{
EndCurrentPass();
MTLComputeCommandEncoder computeCommandEncoder = _encoderFactory.CreateComputeCommandEncoder();
CurrentEncoder = computeCommandEncoder;
CurrentEncoderType = EncoderType.Compute;
return computeCommandEncoder;
}
}

View File

@@ -0,0 +1,289 @@
using SharpMetal.Metal;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.Versioning;
using System.Threading;
namespace Ryujinx.Graphics.Metal
{
[SupportedOSPlatform("macos")]
class CommandBufferPool : IDisposable
{
public const int MaxCommandBuffers = 16;
private readonly int _totalCommandBuffers;
private readonly int _totalCommandBuffersMask;
private readonly MTLCommandQueue _queue;
private readonly Thread _owner;
private IEncoderFactory _defaultEncoderFactory;
public bool OwnedByCurrentThread => _owner == Thread.CurrentThread;
[SupportedOSPlatform("macos")]
private struct ReservedCommandBuffer
{
public bool InUse;
public bool InConsumption;
public int SubmissionCount;
public MTLCommandBuffer CommandBuffer;
public CommandBufferEncoder Encoders;
public FenceHolder Fence;
public List<IAuto> Dependants;
public List<MultiFenceHolder> Waitables;
public void Use(MTLCommandQueue queue, IEncoderFactory stateManager)
{
MTLCommandBufferDescriptor descriptor = new();
#if DEBUG
descriptor.ErrorOptions = MTLCommandBufferErrorOption.EncoderExecutionStatus;
#endif
CommandBuffer = queue.CommandBuffer(descriptor);
Fence = new FenceHolder(CommandBuffer);
Encoders.Initialize(CommandBuffer, stateManager);
InUse = true;
}
public void Initialize()
{
Dependants = [];
Waitables = [];
Encoders = new CommandBufferEncoder();
}
}
private readonly ReservedCommandBuffer[] _commandBuffers;
private readonly int[] _queuedIndexes;
private int _queuedIndexesPtr;
private int _queuedCount;
private int _inUseCount;
public CommandBufferPool(MTLCommandQueue queue, bool isLight = false)
{
_queue = queue;
_owner = Thread.CurrentThread;
_totalCommandBuffers = isLight ? 2 : MaxCommandBuffers;
_totalCommandBuffersMask = _totalCommandBuffers - 1;
_commandBuffers = new ReservedCommandBuffer[_totalCommandBuffers];
_queuedIndexes = new int[_totalCommandBuffers];
_queuedIndexesPtr = 0;
_queuedCount = 0;
}
public void Initialize(IEncoderFactory encoderFactory)
{
_defaultEncoderFactory = encoderFactory;
for (int i = 0; i < _totalCommandBuffers; i++)
{
_commandBuffers[i].Initialize();
WaitAndDecrementRef(i);
}
}
public void AddDependant(int cbIndex, IAuto dependant)
{
dependant.IncrementReferenceCount();
_commandBuffers[cbIndex].Dependants.Add(dependant);
}
public void AddWaitable(MultiFenceHolder waitable)
{
lock (_commandBuffers)
{
for (int i = 0; i < _totalCommandBuffers; i++)
{
ref ReservedCommandBuffer entry = ref _commandBuffers[i];
if (entry.InConsumption)
{
AddWaitable(i, waitable);
}
}
}
}
public void AddInUseWaitable(MultiFenceHolder waitable)
{
lock (_commandBuffers)
{
for (int i = 0; i < _totalCommandBuffers; i++)
{
ref ReservedCommandBuffer entry = ref _commandBuffers[i];
if (entry.InUse)
{
AddWaitable(i, waitable);
}
}
}
}
public void AddWaitable(int cbIndex, MultiFenceHolder waitable)
{
ref ReservedCommandBuffer entry = ref _commandBuffers[cbIndex];
if (waitable.AddFence(cbIndex, entry.Fence))
{
entry.Waitables.Add(waitable);
}
}
public bool IsFenceOnRentedCommandBuffer(FenceHolder fence)
{
lock (_commandBuffers)
{
for (int i = 0; i < _totalCommandBuffers; i++)
{
ref ReservedCommandBuffer entry = ref _commandBuffers[i];
if (entry.InUse && entry.Fence == fence)
{
return true;
}
}
}
return false;
}
public FenceHolder GetFence(int cbIndex)
{
return _commandBuffers[cbIndex].Fence;
}
public int GetSubmissionCount(int cbIndex)
{
return _commandBuffers[cbIndex].SubmissionCount;
}
private int FreeConsumed(bool wait)
{
int freeEntry = 0;
while (_queuedCount > 0)
{
int index = _queuedIndexes[_queuedIndexesPtr];
ref ReservedCommandBuffer entry = ref _commandBuffers[index];
if (wait || !entry.InConsumption || entry.Fence.IsSignaled())
{
WaitAndDecrementRef(index);
wait = false;
freeEntry = index;
_queuedCount--;
_queuedIndexesPtr = (_queuedIndexesPtr + 1) % _totalCommandBuffers;
}
else
{
break;
}
}
return freeEntry;
}
public CommandBufferScoped ReturnAndRent(CommandBufferScoped cbs)
{
Return(cbs);
return Rent();
}
public CommandBufferScoped Rent()
{
lock (_commandBuffers)
{
int cursor = FreeConsumed(_inUseCount + _queuedCount == _totalCommandBuffers);
for (int i = 0; i < _totalCommandBuffers; i++)
{
ref ReservedCommandBuffer entry = ref _commandBuffers[cursor];
if (!entry.InUse && !entry.InConsumption)
{
entry.Use(_queue, _defaultEncoderFactory);
_inUseCount++;
return new CommandBufferScoped(this, entry.CommandBuffer, entry.Encoders, cursor);
}
cursor = (cursor + 1) & _totalCommandBuffersMask;
}
}
throw new InvalidOperationException($"Out of command buffers (In use: {_inUseCount}, queued: {_queuedCount}, total: {_totalCommandBuffers})");
}
public void Return(CommandBufferScoped cbs)
{
// Ensure the encoder is committed.
cbs.Encoders.EndCurrentPass();
lock (_commandBuffers)
{
int cbIndex = cbs.CommandBufferIndex;
ref ReservedCommandBuffer entry = ref _commandBuffers[cbIndex];
Debug.Assert(entry.InUse);
Debug.Assert(entry.CommandBuffer.NativePtr == cbs.CommandBuffer.NativePtr);
entry.InUse = false;
entry.InConsumption = true;
entry.SubmissionCount++;
_inUseCount--;
MTLCommandBuffer commandBuffer = entry.CommandBuffer;
commandBuffer.Commit();
int ptr = (_queuedIndexesPtr + _queuedCount) % _totalCommandBuffers;
_queuedIndexes[ptr] = cbIndex;
_queuedCount++;
}
}
private void WaitAndDecrementRef(int cbIndex)
{
ref ReservedCommandBuffer entry = ref _commandBuffers[cbIndex];
if (entry.InConsumption)
{
entry.Fence.Wait();
entry.InConsumption = false;
}
foreach (IAuto dependant in entry.Dependants)
{
dependant.DecrementReferenceCount(cbIndex);
}
foreach (MultiFenceHolder waitable in entry.Waitables)
{
waitable.RemoveFence(cbIndex);
waitable.RemoveBufferUses(cbIndex);
}
entry.Dependants.Clear();
entry.Waitables.Clear();
entry.Fence?.Dispose();
}
public void Dispose()
{
for (int i = 0; i < _totalCommandBuffers; i++)
{
WaitAndDecrementRef(i);
}
}
}
}

View File

@@ -0,0 +1,43 @@
using SharpMetal.Metal;
using System;
using System.Runtime.Versioning;
namespace Ryujinx.Graphics.Metal
{
[SupportedOSPlatform("macos")]
readonly struct CommandBufferScoped : IDisposable
{
private readonly CommandBufferPool _pool;
public MTLCommandBuffer CommandBuffer { get; }
public CommandBufferEncoder Encoders { get; }
public int CommandBufferIndex { get; }
public CommandBufferScoped(CommandBufferPool pool, MTLCommandBuffer commandBuffer, CommandBufferEncoder encoders, int commandBufferIndex)
{
_pool = pool;
CommandBuffer = commandBuffer;
Encoders = encoders;
CommandBufferIndex = commandBufferIndex;
}
public void AddDependant(IAuto dependant)
{
_pool.AddDependant(CommandBufferIndex, dependant);
}
public void AddWaitable(MultiFenceHolder waitable)
{
_pool.AddWaitable(CommandBufferIndex, waitable);
}
public FenceHolder GetFence()
{
return _pool.GetFence(CommandBufferIndex);
}
public void Dispose()
{
_pool?.Return(this);
}
}
}

View File

@@ -0,0 +1,41 @@
namespace Ryujinx.Graphics.Metal
{
static class Constants
{
public const int MaxShaderStages = 5;
public const int MaxVertexBuffers = 16;
public const int MaxUniformBuffersPerStage = 18;
public const int MaxStorageBuffersPerStage = 16;
public const int MaxTexturesPerStage = 64;
public const int MaxImagesPerStage = 16;
public const int MaxUniformBufferBindings = MaxUniformBuffersPerStage * MaxShaderStages;
public const int MaxStorageBufferBindings = MaxStorageBuffersPerStage * MaxShaderStages;
public const int MaxTextureBindings = MaxTexturesPerStage * MaxShaderStages;
public const int MaxImageBindings = MaxImagesPerStage * MaxShaderStages;
public const int MaxColorAttachments = 8;
public const int MaxViewports = 16;
// TODO: Check this value
public const int MaxVertexAttributes = 31;
public const int MinResourceAlignment = 16;
// Must match constants set in shader generation
public const uint ZeroBufferIndex = MaxVertexBuffers;
public const uint BaseSetIndex = MaxVertexBuffers + 1;
public const uint ConstantBuffersIndex = BaseSetIndex;
public const uint StorageBuffersIndex = BaseSetIndex + 1;
public const uint TexturesIndex = BaseSetIndex + 2;
public const uint ImagesIndex = BaseSetIndex + 3;
public const uint ConstantBuffersSetIndex = 0;
public const uint StorageBuffersSetIndex = 1;
public const uint TexturesSetIndex = 2;
public const uint ImagesSetIndex = 3;
public const uint MaximumBufferArgumentTableEntries = 31;
public const uint MaximumExtraSets = MaximumBufferArgumentTableEntries - ImagesIndex;
}
}

View File

@@ -0,0 +1,22 @@
using Ryujinx.Graphics.GAL;
namespace Ryujinx.Graphics.Metal
{
class CounterEvent : ICounterEvent
{
public CounterEvent()
{
Invalid = false;
}
public bool Invalid { get; set; }
public bool ReserveForHostAccess()
{
return true;
}
public void Flush() { }
public void Dispose() { }
}
}

Some files were not shown because too many files have changed in this diff Show More