Compare commits
149 Commits
Canary-1.2
...
feature/na
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
206452e655 | ||
|
|
57c22a1f32 | ||
|
|
f7976753fd | ||
|
|
b45a65fbdc | ||
|
|
c410474d83 | ||
|
|
ffdc419417 | ||
|
|
da3f4e1d3a | ||
|
|
69d79322bb | ||
|
|
c3af1dbf1a | ||
|
|
10ac381525 | ||
|
|
6c159372f5 | ||
|
|
39ced98cb6 | ||
|
|
5a1476148f | ||
|
|
d304779938 | ||
|
|
c3083076ee | ||
|
|
ccfcdd4219 | ||
|
|
64a283c4e6 | ||
|
|
2c15b1ccb6 | ||
|
|
e104ee6be3 | ||
|
|
e65d1ec6c9 | ||
|
|
534f92506b | ||
|
|
10d20c1ae3 | ||
|
|
e294a79975 | ||
|
|
ec06a86899 | ||
|
|
2e4de17472 | ||
|
|
9227cbe5a7 | ||
|
|
332bcdfaf1 | ||
|
|
1c8276197f | ||
|
|
a3596ba858 | ||
|
|
3ffcc72117 | ||
|
|
fe1617ffea | ||
|
|
eb6b0e9adc | ||
|
|
9631bdfe16 | ||
|
|
2a84656ffc | ||
|
|
6c6580ddcc | ||
|
|
c47448628c | ||
|
|
d0ac83b493 | ||
|
|
e0ddbe55c0 | ||
|
|
4a4078865f | ||
|
|
3f59bade94 | ||
|
|
c2ed0fd5fd | ||
|
|
de16d8fa3e | ||
|
|
9b1fb3a27b | ||
|
|
b9150a0092 | ||
|
|
c1002d4826 | ||
|
|
b1de7696ee | ||
|
|
f91cd05260 | ||
|
|
920933bc9f | ||
|
|
52b0b45d34 | ||
|
|
12f0dbcc70 | ||
|
|
719be560ec | ||
|
|
18238736be | ||
|
|
5d9e4ad7a4 | ||
|
|
adba775f0c | ||
|
|
2ffaeb2803 | ||
|
|
b16b844760 | ||
|
|
bc07bc482d | ||
|
|
61975ca44d | ||
|
|
66054dd225 | ||
|
|
b1f61e5143 | ||
|
|
0d7d0e8092 | ||
|
|
aa2178dbe5 | ||
|
|
f92d09711b | ||
|
|
45ee8cd0e8 | ||
|
|
395bbd144a | ||
|
|
744d813b87 | ||
|
|
7d59ada798 | ||
|
|
a4b5304935 | ||
|
|
0965ee905d | ||
|
|
855161b23b | ||
|
|
6b55d158b7 | ||
|
|
91f73a4891 | ||
|
|
883d4d863a | ||
|
|
ca5de909a1 | ||
|
|
5172567b08 | ||
|
|
6fe4cee7c0 | ||
|
|
8623452abc | ||
|
|
17e8ae1d9a | ||
|
|
7591b07fce | ||
|
|
89b4389ed2 | ||
|
|
d9ee729199 | ||
|
|
ba0cd13cff | ||
|
|
501b199e24 | ||
|
|
8aecccadb8 | ||
|
|
e23d610f49 | ||
|
|
f6822f7358 | ||
|
|
d3f84a1305 | ||
|
|
06d34a5992 | ||
|
|
e8e1dc6619 | ||
|
|
c5603d4c36 | ||
|
|
05b56730d6 | ||
|
|
43f7b000ca | ||
|
|
ad89cf39b6 | ||
|
|
96c33a0b92 | ||
|
|
e0db55df46 | ||
|
|
30fef8e96e | ||
|
|
9cb5f5689b | ||
|
|
a205ec374b | ||
|
|
aab9b58542 | ||
|
|
daa648dc40 | ||
|
|
1024aa8757 | ||
|
|
13388e972a | ||
|
|
1eb78872d8 | ||
|
|
fe9fe2a10f | ||
|
|
6ab899f621 | ||
|
|
faacec9801 | ||
|
|
55fdb3f6b2 | ||
|
|
1129ab0e8c | ||
|
|
b6b391b2cf | ||
|
|
f3cf03495d | ||
|
|
7bce8206d5 | ||
|
|
efa0cc7554 | ||
|
|
1c0813d09d | ||
|
|
8bec09d7ff | ||
|
|
e4b4e94b56 | ||
|
|
764c9e9d4e | ||
|
|
05e991db87 | ||
|
|
2cd876b1cb | ||
|
|
93a298523f | ||
|
|
253cbb2810 | ||
|
|
9c226dcc7a | ||
|
|
30a534edcd | ||
|
|
1d88771d1b | ||
|
|
4e8157688e | ||
|
|
5085af0050 | ||
|
|
2c8edaf89e | ||
|
|
aa8ba8b503 | ||
|
|
a4211fec33 | ||
|
|
54b233dd78 | ||
|
|
d1da937fce | ||
|
|
4a8f98126f | ||
|
|
e55629a908 | ||
|
|
c638a7daf8 | ||
|
|
5e5e180fea | ||
|
|
131fe71205 | ||
|
|
6af388c623 | ||
|
|
45cec4e7cf | ||
|
|
479b38f035 | ||
|
|
3ecc7819cc | ||
|
|
4b1d94ccd8 | ||
|
|
4ae9f1c0d2 | ||
|
|
717851985e | ||
|
|
bd08a111a8 | ||
|
|
1972a47f39 | ||
|
|
222ceb818b | ||
|
|
b0fcc5bee1 | ||
|
|
820e8f7375 | ||
|
|
e8a7d5b0b7 | ||
|
|
fafb99c702 |
4
.github/workflows/canary.yml
vendored
4
.github/workflows/canary.yml
vendored
@@ -29,7 +29,7 @@ env:
|
||||
jobs:
|
||||
tag:
|
||||
name: Create tag
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Get version info
|
||||
id: version_info
|
||||
@@ -202,7 +202,7 @@ jobs:
|
||||
|
||||
macos_release:
|
||||
name: Release MacOS universal
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
|
||||
4
.github/workflows/release.yml
vendored
4
.github/workflows/release.yml
vendored
@@ -18,7 +18,7 @@ env:
|
||||
jobs:
|
||||
tag:
|
||||
name: Create tag
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Get version info
|
||||
id: version_info
|
||||
@@ -183,7 +183,7 @@ jobs:
|
||||
|
||||
macos_release:
|
||||
name: Release MacOS universal
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
|
||||
@@ -42,11 +42,10 @@
|
||||
<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" />
|
||||
<PackageVersion Include="Gommon" Version="2.7.1.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" />
|
||||
|
||||
15
README.md
15
README.md
@@ -1,3 +1,12 @@
|
||||
# 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%">
|
||||
@@ -39,12 +48,12 @@
|
||||
<p align="center">
|
||||
Click below to join the Discord:
|
||||
<br>
|
||||
<a href="https://discord.gg/dHPrkBkkyA">
|
||||
<a href="https://discord.gg/PEuzjrFXUA">
|
||||
<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/GreemDev/Ryujinx/refs/heads/master/docs/shell.png">
|
||||
<img src="https://raw.githubusercontent.com/Ryubing/Ryujinx/refs/heads/master/docs/shell.png">
|
||||
</p>
|
||||
|
||||
## Usage
|
||||
@@ -97,7 +106,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, JoyCon input support, and nearly all controllers.
|
||||
We currently have support for keyboard, mouse, touch input, Joy-Con 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.
|
||||
|
||||
|
||||
182
Ryujinx.sln
182
Ryujinx.sln
@@ -77,15 +77,6 @@ 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
|
||||
@@ -95,170 +86,327 @@ 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
|
||||
{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
|
||||
{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
|
||||
{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
|
||||
{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
|
||||
{4A89A234-4F19-497D-A576-DDE8CDFC5B22}.ReleaseAOT|Any CPU.ActiveCfg = ReleaseAOT|Any CPU
|
||||
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
@@ -332,6 +332,7 @@
|
||||
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
|
||||
@@ -426,6 +427,7 @@
|
||||
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
|
||||
@@ -528,6 +530,7 @@
|
||||
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
|
||||
@@ -628,6 +631,7 @@
|
||||
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
|
||||
@@ -955,7 +959,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,menus,2023-08-13 17:20:03
|
||||
010027400CDC6000,"Divinity: Original Sin 2 - Definitive Edition",services;crash;online-broken;regression,ingame,2025-02-03 22:12:30
|
||||
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
|
||||
@@ -966,6 +970,7 @@
|
||||
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
|
||||
@@ -1155,7 +1160,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",crash,nothing,2024-09-05 20:55:30
|
||||
01000EA014150000,"FINAL FANTASY",,playable,2025-02-16 21:27: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
|
||||
@@ -1246,7 +1251,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,playable,2022-10-17 20:58:32
|
||||
0100E1F013674000,"FUSER™",nvdec;UE4;slow;gpu,ingame,2025-02-12 16:03:00
|
||||
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
|
||||
@@ -1378,6 +1383,9 @@
|
||||
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
|
||||
@@ -1432,7 +1440,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",slow,ingame,2024-09-03 19:53:58
|
||||
0100F7E00C70E000,"Hogwarts Legacy",UE4;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
|
||||
@@ -1654,7 +1662,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
|
||||
01003A30012C0000,"LEGO® CITY Undercover",nvdec,playable,2024-09-30 08:44:27
|
||||
010085500130a000,"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
|
||||
@@ -1796,6 +1804,7 @@
|
||||
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
|
||||
@@ -1913,6 +1922,7 @@
|
||||
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
|
||||
@@ -2028,6 +2038,7 @@
|
||||
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
|
||||
@@ -2058,7 +2069,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
|
||||
0000000000000000,"nx-hbmenu",Needs Update;homebrew,boots,2024-04-06 22:05:32
|
||||
,"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
|
||||
@@ -2466,7 +2477,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
|
||||
0000000000000000,"RSDKv5u",homebrew,ingame,2024-04-01 16:25:34
|
||||
,"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
|
||||
@@ -2475,6 +2486,7 @@
|
||||
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
|
||||
@@ -2532,7 +2544,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,nothing,2022-04-11 07:27:21
|
||||
0100B3C014BDA000,"SEGA Genesis™ – Nintendo Switch Online",crash;regression,ingame,2025-02-03 22:13:30
|
||||
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
|
||||
@@ -2668,10 +2680,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
|
||||
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
|
||||
,"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
|
||||
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
|
||||
@@ -2688,7 +2700,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
|
||||
0000000000000000,"SpaceCadetPinball",homebrew,ingame,2024-04-18 19:30:04
|
||||
,"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
|
||||
@@ -2721,7 +2733,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,2023-08-01 19:29:53
|
||||
01009FB0172F4000,"SpongeBob SquarePants: The Cosmic Shake",gpu;UE4,ingame,2024-03-04 16:35:00
|
||||
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
|
||||
@@ -2832,8 +2844,9 @@
|
||||
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
|
||||
0000000000000000,"Super Mario World",homebrew,boots,2024-06-13 01:40:31
|
||||
,"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 + Bowser’s Fury",ldn-works,playable,2024-07-31 10:45:37
|
||||
01004F8006A78000,"Super Meat Boy",services,playable,2020-04-02 23:10:07
|
||||
@@ -2964,6 +2977,7 @@
|
||||
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
|
||||
@@ -2981,6 +2995,8 @@
|
||||
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
|
||||
@@ -3159,6 +3175,7 @@
|
||||
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
|
||||
@@ -3208,6 +3225,7 @@
|
||||
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
|
||||
@@ -3372,6 +3390,7 @@
|
||||
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
|
||||
|
||||
|
@@ -3,6 +3,7 @@
|
||||
<PropertyGroup>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
<Configurations>Debug;Release;DebugAOT;ReleaseAOT</Configurations>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -7,15 +7,17 @@ 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(typeof(NativeInterface).GetMethod(name), Const(op.Address), Const(op.Id));
|
||||
context.Call(NativeInterface.Type.GetMethod(BreakName), Const(op.Address), Const(op.Id));
|
||||
|
||||
context.LoadFromContext();
|
||||
|
||||
@@ -26,11 +28,9 @@ namespace ARMeilleure.Instructions
|
||||
{
|
||||
OpCodeException op = (OpCodeException)context.CurrOp;
|
||||
|
||||
string name = nameof(NativeInterface.SupervisorCall);
|
||||
|
||||
context.StoreToContext();
|
||||
|
||||
context.Call(typeof(NativeInterface).GetMethod(name), Const(op.Address), Const(op.Id));
|
||||
context.Call(NativeInterface.Type.GetMethod(SupervisorCallName), Const(op.Address), Const(op.Id));
|
||||
|
||||
context.LoadFromContext();
|
||||
|
||||
@@ -41,11 +41,9 @@ namespace ARMeilleure.Instructions
|
||||
{
|
||||
OpCode op = context.CurrOp;
|
||||
|
||||
string name = nameof(NativeInterface.Undefined);
|
||||
|
||||
context.StoreToContext();
|
||||
|
||||
context.Call(typeof(NativeInterface).GetMethod(name), Const(op.Address), Const(op.RawOpCode));
|
||||
context.Call(NativeInterface.Type.GetMethod(UndefinedName), Const(op.Address), Const(op.RawOpCode));
|
||||
|
||||
context.LoadFromContext();
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
context.StoreToContext();
|
||||
|
||||
context.Call(typeof(NativeInterface).GetMethod(name), Const(((IOpCode)op).Address), Const(op.Id));
|
||||
context.Call(NativeInterface.Type.GetMethod(name), Const(((IOpCode)op).Address), Const(op.Id));
|
||||
|
||||
context.LoadFromContext();
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
context.StoreToContext();
|
||||
|
||||
context.Call(typeof(NativeInterface).GetMethod(name), Const(((IOpCode)op).Address), Const(op.Id));
|
||||
context.Call(NativeInterface.Type.GetMethod(name), Const(((IOpCode)op).Address), Const(op.Id));
|
||||
|
||||
context.LoadFromContext();
|
||||
|
||||
|
||||
@@ -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(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SignalMemoryTracking)), address, Const(1UL), Const(write ? 1 : 0));
|
||||
context.Call(NativeInterface.Type.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(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ThrowInvalidMemoryAccess)), address);
|
||||
context.Call(NativeInterface.Type.GetMethod(nameof(NativeInterface.ThrowInvalidMemoryAccess)), address);
|
||||
context.MarkLabel(lblNonNull);
|
||||
}
|
||||
|
||||
@@ -535,16 +535,16 @@ namespace ARMeilleure.Instructions
|
||||
switch (size)
|
||||
{
|
||||
case 0:
|
||||
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadByte));
|
||||
info = NativeInterface.Type.GetMethod(nameof(NativeInterface.ReadByte));
|
||||
break;
|
||||
case 1:
|
||||
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt16));
|
||||
info = NativeInterface.Type.GetMethod(nameof(NativeInterface.ReadUInt16));
|
||||
break;
|
||||
case 2:
|
||||
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt32));
|
||||
info = NativeInterface.Type.GetMethod(nameof(NativeInterface.ReadUInt32));
|
||||
break;
|
||||
case 3:
|
||||
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt64));
|
||||
info = NativeInterface.Type.GetMethod(nameof(NativeInterface.ReadUInt64));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -564,19 +564,19 @@ namespace ARMeilleure.Instructions
|
||||
switch (size)
|
||||
{
|
||||
case 0:
|
||||
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadByte));
|
||||
info = NativeInterface.Type.GetMethod(nameof(NativeInterface.ReadByte));
|
||||
break;
|
||||
case 1:
|
||||
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt16));
|
||||
info = NativeInterface.Type.GetMethod(nameof(NativeInterface.ReadUInt16));
|
||||
break;
|
||||
case 2:
|
||||
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt32));
|
||||
info = NativeInterface.Type.GetMethod(nameof(NativeInterface.ReadUInt32));
|
||||
break;
|
||||
case 3:
|
||||
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt64));
|
||||
info = NativeInterface.Type.GetMethod(nameof(NativeInterface.ReadUInt64));
|
||||
break;
|
||||
case 4:
|
||||
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadVector128));
|
||||
info = NativeInterface.Type.GetMethod(nameof(NativeInterface.ReadVector128));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -608,16 +608,16 @@ namespace ARMeilleure.Instructions
|
||||
switch (size)
|
||||
{
|
||||
case 0:
|
||||
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteByte));
|
||||
info = NativeInterface.Type.GetMethod(nameof(NativeInterface.WriteByte));
|
||||
break;
|
||||
case 1:
|
||||
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt16));
|
||||
info = NativeInterface.Type.GetMethod(nameof(NativeInterface.WriteUInt16));
|
||||
break;
|
||||
case 2:
|
||||
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt32));
|
||||
info = NativeInterface.Type.GetMethod(nameof(NativeInterface.WriteUInt32));
|
||||
break;
|
||||
case 3:
|
||||
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt64));
|
||||
info = NativeInterface.Type.GetMethod(nameof(NativeInterface.WriteUInt64));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -643,19 +643,19 @@ namespace ARMeilleure.Instructions
|
||||
switch (size)
|
||||
{
|
||||
case 0:
|
||||
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteByte));
|
||||
info = NativeInterface.Type.GetMethod(nameof(NativeInterface.WriteByte));
|
||||
break;
|
||||
case 1:
|
||||
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt16));
|
||||
info = NativeInterface.Type.GetMethod(nameof(NativeInterface.WriteUInt16));
|
||||
break;
|
||||
case 2:
|
||||
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt32));
|
||||
info = NativeInterface.Type.GetMethod(nameof(NativeInterface.WriteUInt32));
|
||||
break;
|
||||
case 3:
|
||||
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt64));
|
||||
info = NativeInterface.Type.GetMethod(nameof(NativeInterface.WriteUInt64));
|
||||
break;
|
||||
case 4:
|
||||
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteVector128));
|
||||
info = NativeInterface.Type.GetMethod(nameof(NativeInterface.WriteVector128));
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -406,7 +406,7 @@ namespace ARMeilleure.Instructions
|
||||
{
|
||||
Operand res = EmitSoftFloatCall(context, nameof(SoftFloat32.FPSub), op1, op2);
|
||||
|
||||
return EmitUnaryMathCall(context, nameof(MathHelper.Abs), res);
|
||||
return EmitUnaryMathCall(context, nameof(Math.Abs), res);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -451,7 +451,7 @@ namespace ARMeilleure.Instructions
|
||||
{
|
||||
Operand res = EmitSoftFloatCall(context, nameof(SoftFloat32.FPSub), op1, op2);
|
||||
|
||||
return EmitUnaryMathCall(context, nameof(MathHelper.Abs), res);
|
||||
return EmitUnaryMathCall(context, nameof(Math.Abs), res);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -483,7 +483,7 @@ namespace ARMeilleure.Instructions
|
||||
{
|
||||
EmitScalarUnaryOpF(context, (op1) =>
|
||||
{
|
||||
return EmitUnaryMathCall(context, nameof(MathHelper.Abs), op1);
|
||||
return EmitUnaryMathCall(context, nameof(Math.Abs), op1);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -522,7 +522,7 @@ namespace ARMeilleure.Instructions
|
||||
{
|
||||
EmitVectorUnaryOpF(context, (op1) =>
|
||||
{
|
||||
return EmitUnaryMathCall(context, nameof(MathHelper.Abs), op1);
|
||||
return EmitUnaryMathCall(context, nameof(Math.Abs), op1);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -2246,7 +2246,7 @@ namespace ARMeilleure.Instructions
|
||||
{
|
||||
EmitScalarUnaryOpF(context, (op1) =>
|
||||
{
|
||||
return EmitUnaryMathCall(context, nameof(MathHelper.Floor), op1);
|
||||
return EmitUnaryMathCall(context, nameof(Math.Floor), op1);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -2265,7 +2265,7 @@ namespace ARMeilleure.Instructions
|
||||
{
|
||||
EmitVectorUnaryOpF(context, (op1) =>
|
||||
{
|
||||
return EmitUnaryMathCall(context, nameof(MathHelper.Floor), op1);
|
||||
return EmitUnaryMathCall(context, nameof(Math.Floor), op1);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -2322,7 +2322,7 @@ namespace ARMeilleure.Instructions
|
||||
{
|
||||
EmitScalarUnaryOpF(context, (op1) =>
|
||||
{
|
||||
return EmitUnaryMathCall(context, nameof(MathHelper.Ceiling), op1);
|
||||
return EmitUnaryMathCall(context, nameof(Math.Ceiling), op1);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -2341,7 +2341,7 @@ namespace ARMeilleure.Instructions
|
||||
{
|
||||
EmitVectorUnaryOpF(context, (op1) =>
|
||||
{
|
||||
return EmitUnaryMathCall(context, nameof(MathHelper.Ceiling), op1);
|
||||
return EmitUnaryMathCall(context, nameof(Math.Ceiling), op1);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -2390,7 +2390,7 @@ namespace ARMeilleure.Instructions
|
||||
{
|
||||
EmitScalarUnaryOpF(context, (op1) =>
|
||||
{
|
||||
return EmitUnaryMathCall(context, nameof(MathHelper.Truncate), op1);
|
||||
return EmitUnaryMathCall(context, nameof(Math.Truncate), op1);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -2409,7 +2409,7 @@ namespace ARMeilleure.Instructions
|
||||
{
|
||||
EmitVectorUnaryOpF(context, (op1) =>
|
||||
{
|
||||
return EmitUnaryMathCall(context, nameof(MathHelper.Truncate), op1);
|
||||
return EmitUnaryMathCall(context, nameof(Math.Truncate), op1);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace ARMeilleure.Instructions
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitScalarUnaryOpF32(context, (op1) => EmitUnaryMathCall(context, nameof(MathHelper.Abs), op1));
|
||||
EmitScalarUnaryOpF32(context, (op1) => EmitUnaryMathCall(context, nameof(Math.Abs), op1));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ namespace ARMeilleure.Instructions
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitVectorUnaryOpF32(context, (op1) => EmitUnaryMathCall(context, nameof(MathHelper.Abs), op1));
|
||||
EmitVectorUnaryOpF32(context, (op1) => EmitUnaryMathCall(context, nameof(Math.Abs), op1));
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -726,8 +726,8 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
if (absolute)
|
||||
{
|
||||
ne = EmitUnaryMathCall(context, nameof(MathHelper.Abs), ne);
|
||||
me = EmitUnaryMathCall(context, nameof(MathHelper.Abs), me);
|
||||
ne = EmitUnaryMathCall(context, nameof(Math.Abs), ne);
|
||||
me = EmitUnaryMathCall(context, nameof(Math.Abs), me);
|
||||
}
|
||||
|
||||
Operand e = EmitSoftFloatCall(context, name, ne, me);
|
||||
|
||||
@@ -333,7 +333,7 @@ namespace ARMeilleure.Instructions
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitFcvt_s_Gp(context, (op1) => EmitUnaryMathCall(context, nameof(MathHelper.Floor), op1));
|
||||
EmitFcvt_s_Gp(context, (op1) => EmitUnaryMathCall(context, nameof(Math.Floor), op1));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -349,7 +349,7 @@ namespace ARMeilleure.Instructions
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitFcvt(context, (op1) => EmitUnaryMathCall(context, nameof(MathHelper.Floor), op1), signed: true, scalar: false);
|
||||
EmitFcvt(context, (op1) => EmitUnaryMathCall(context, nameof(Math.Floor), op1), signed: true, scalar: false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -365,7 +365,7 @@ namespace ARMeilleure.Instructions
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitFcvt_u_Gp(context, (op1) => EmitUnaryMathCall(context, nameof(MathHelper.Floor), op1));
|
||||
EmitFcvt_u_Gp(context, (op1) => EmitUnaryMathCall(context, nameof(Math.Floor), op1));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -538,7 +538,7 @@ namespace ARMeilleure.Instructions
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitFcvt_s_Gp(context, (op1) => EmitUnaryMathCall(context, nameof(MathHelper.Ceiling), op1));
|
||||
EmitFcvt_s_Gp(context, (op1) => EmitUnaryMathCall(context, nameof(Math.Ceiling), op1));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -554,7 +554,7 @@ namespace ARMeilleure.Instructions
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitFcvt_u_Gp(context, (op1) => EmitUnaryMathCall(context, nameof(MathHelper.Ceiling), op1));
|
||||
EmitFcvt_u_Gp(context, (op1) => EmitUnaryMathCall(context, nameof(Math.Ceiling), op1));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -357,10 +357,10 @@ namespace ARMeilleure.Instructions
|
||||
toConvert = EmitRoundMathCall(context, MidpointRounding.ToEven, toConvert);
|
||||
break;
|
||||
case 0b10: // Towards positive infinity
|
||||
toConvert = EmitUnaryMathCall(context, nameof(MathHelper.Ceiling), toConvert);
|
||||
toConvert = EmitUnaryMathCall(context, nameof(Math.Ceiling), toConvert);
|
||||
break;
|
||||
case 0b11: // Towards negative infinity
|
||||
toConvert = EmitUnaryMathCall(context, nameof(MathHelper.Floor), toConvert);
|
||||
toConvert = EmitUnaryMathCall(context, nameof(Math.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(MathHelper.Ceiling), toConvert);
|
||||
toConvert = EmitUnaryMathCall(context, nameof(Math.Ceiling), toConvert);
|
||||
break;
|
||||
case 0b11: // Towards negative infinity
|
||||
toConvert = EmitUnaryMathCall(context, nameof(MathHelper.Floor), toConvert);
|
||||
toConvert = EmitUnaryMathCall(context, nameof(Math.Floor), toConvert);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -534,7 +534,7 @@ namespace ARMeilleure.Instructions
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitVectorUnaryOpF32(context, (m) => EmitUnaryMathCall(context, nameof(MathHelper.Floor), m));
|
||||
EmitVectorUnaryOpF32(context, (m) => EmitUnaryMathCall(context, nameof(Math.Floor), m));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -574,7 +574,7 @@ namespace ARMeilleure.Instructions
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitVectorUnaryOpF32(context, (m) => EmitUnaryMathCall(context, nameof(MathHelper.Ceiling), m));
|
||||
EmitVectorUnaryOpF32(context, (m) => EmitUnaryMathCall(context, nameof(Math.Ceiling), m));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -613,7 +613,7 @@ namespace ARMeilleure.Instructions
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitScalarUnaryOpF32(context, (op1) => EmitUnaryMathCall(context, nameof(MathHelper.Truncate), op1));
|
||||
EmitScalarUnaryOpF32(context, (op1) => EmitUnaryMathCall(context, nameof(Math.Truncate), op1));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -460,8 +460,8 @@ namespace ARMeilleure.Instructions
|
||||
IOpCodeSimd op = (IOpCodeSimd)context.CurrOp;
|
||||
|
||||
MethodInfo info = (op.Size & 1) == 0
|
||||
? typeof(MathHelperF).GetMethod(name, [typeof(float)])
|
||||
: typeof(MathHelper).GetMethod(name, [typeof(double)]);
|
||||
? typeof(MathF).GetMethod(name, [typeof(float)])
|
||||
: typeof(Math).GetMethod(name, [typeof(double)]);
|
||||
|
||||
return context.Call(info, n);
|
||||
}
|
||||
@@ -470,11 +470,11 @@ namespace ARMeilleure.Instructions
|
||||
{
|
||||
IOpCodeSimd op = (IOpCodeSimd)context.CurrOp;
|
||||
|
||||
string name = nameof(MathHelper.Round);
|
||||
string name = nameof(Math.Round);
|
||||
|
||||
MethodInfo info = (op.Size & 1) == 0
|
||||
? typeof(MathHelperF).GetMethod(name, [typeof(float), typeof(int)])
|
||||
: typeof(MathHelper).GetMethod(name, [typeof(double), typeof(int)]);
|
||||
? typeof(MathF).GetMethod(name, [typeof(float), typeof(MidpointRounding)])
|
||||
: typeof(Math).GetMethod(name, [typeof(double), typeof(MidpointRounding)]);
|
||||
|
||||
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(MathHelper.Ceiling), op));
|
||||
context.Copy(res, EmitUnaryMathCall(context, nameof(Math.Ceiling), op));
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lbl2);
|
||||
context.BranchIf(lbl3, rMode, rM, Comparison.NotEqual);
|
||||
context.Copy(res, EmitUnaryMathCall(context, nameof(MathHelper.Floor), op));
|
||||
context.Copy(res, EmitUnaryMathCall(context, nameof(Math.Floor), op));
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lbl3);
|
||||
context.Copy(res, EmitUnaryMathCall(context, nameof(MathHelper.Truncate), op));
|
||||
context.Copy(res, EmitUnaryMathCall(context, nameof(Math.Truncate), op));
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lblEnd);
|
||||
|
||||
@@ -29,10 +29,10 @@ namespace ARMeilleure.Instructions
|
||||
switch (GetPackedId(op))
|
||||
{
|
||||
case 0b11_011_0000_0000_001:
|
||||
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCtrEl0));
|
||||
info = NativeInterface.Type.GetMethod(nameof(NativeInterface.GetCtrEl0));
|
||||
break;
|
||||
case 0b11_011_0000_0000_111:
|
||||
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetDczidEl0));
|
||||
info = NativeInterface.Type.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 = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntfrqEl0));
|
||||
info = NativeInterface.Type.GetMethod(nameof(NativeInterface.GetCntfrqEl0));
|
||||
break;
|
||||
case 0b11_011_1110_0000_001:
|
||||
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntpctEl0));
|
||||
info = NativeInterface.Type.GetMethod(nameof(NativeInterface.GetCntpctEl0));
|
||||
break;
|
||||
case 0b11_011_1110_0000_010:
|
||||
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntvctEl0));
|
||||
info = NativeInterface.Type.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(typeof(NativeInterface).GetMethod(nameof(NativeInterface.InvalidateCacheLine)), target);
|
||||
context.Call(NativeInterface.Type.GetMethod(nameof(NativeInterface.InvalidateCacheLine)), target);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,7 +127,7 @@ namespace ARMeilleure.Instructions
|
||||
// Timer.
|
||||
14 => opc switch
|
||||
{
|
||||
0 => typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntpctEl0)),
|
||||
0 => NativeInterface.Type.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}."),
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,12 +2,15 @@ using ARMeilleure.Memory;
|
||||
using ARMeilleure.State;
|
||||
using ARMeilleure.Translation;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static class NativeInterface
|
||||
{
|
||||
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]
|
||||
public static readonly Type Type = typeof(NativeInterface);
|
||||
|
||||
private class ThreadContext
|
||||
{
|
||||
public ExecutionContext Context { get; }
|
||||
@@ -35,7 +38,6 @@ namespace ARMeilleure.Instructions
|
||||
Context = null;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static void Break(ulong address, int imm)
|
||||
{
|
||||
Statistics.PauseTimer();
|
||||
@@ -45,7 +47,6 @@ namespace ARMeilleure.Instructions
|
||||
Statistics.ResumeTimer();
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static void SupervisorCall(ulong address, int imm)
|
||||
{
|
||||
Statistics.PauseTimer();
|
||||
@@ -55,7 +56,6 @@ namespace ARMeilleure.Instructions
|
||||
Statistics.ResumeTimer();
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static void Undefined(ulong address, int opCode)
|
||||
{
|
||||
Statistics.PauseTimer();
|
||||
@@ -66,31 +66,26 @@ 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;
|
||||
@@ -98,31 +93,26 @@ 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);
|
||||
@@ -130,56 +120,47 @@ 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);
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static void SignalMemoryTracking(ulong address, ulong size, byte write)
|
||||
public static void SignalMemoryTracking(ulong address, ulong size, bool write)
|
||||
{
|
||||
GetMemoryManager().SignalMemoryTracking(address, size, write == 1);
|
||||
GetMemoryManager().SignalMemoryTracking(address, size, write);
|
||||
}
|
||||
|
||||
[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);
|
||||
@@ -187,14 +168,12 @@ namespace ARMeilleure.Instructions
|
||||
return (ulong)function.FuncPointer.ToInt64();
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static void InvalidateCacheLine(ulong address)
|
||||
{
|
||||
Context.Translator.InvalidateJitCacheRegion(address, InstEmit.DczSizeInBytes);
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static byte CheckSynchronization()
|
||||
public static bool CheckSynchronization()
|
||||
{
|
||||
Statistics.PauseTimer();
|
||||
|
||||
@@ -204,7 +183,7 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
Statistics.ResumeTimer();
|
||||
|
||||
return (byte)(context.Running ? 1 : 0);
|
||||
return context.Running;
|
||||
}
|
||||
|
||||
public static ExecutionContext GetContext()
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
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)
|
||||
@@ -50,7 +48,6 @@ namespace ARMeilleure.Instructions
|
||||
}
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static ulong UnsignedShrImm64(ulong value, long roundConst, int shift)
|
||||
{
|
||||
if (roundConst == 0L)
|
||||
@@ -95,7 +92,6 @@ namespace ARMeilleure.Instructions
|
||||
#endregion
|
||||
|
||||
#region "Saturation"
|
||||
[UnmanagedCallersOnly]
|
||||
public static int SatF32ToS32(float value)
|
||||
{
|
||||
if (float.IsNaN(value))
|
||||
@@ -107,7 +103,6 @@ namespace ARMeilleure.Instructions
|
||||
value <= int.MinValue ? int.MinValue : (int)value;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static long SatF32ToS64(float value)
|
||||
{
|
||||
if (float.IsNaN(value))
|
||||
@@ -119,7 +114,6 @@ namespace ARMeilleure.Instructions
|
||||
value <= long.MinValue ? long.MinValue : (long)value;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static uint SatF32ToU32(float value)
|
||||
{
|
||||
if (float.IsNaN(value))
|
||||
@@ -131,7 +125,6 @@ namespace ARMeilleure.Instructions
|
||||
value <= uint.MinValue ? uint.MinValue : (uint)value;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static ulong SatF32ToU64(float value)
|
||||
{
|
||||
if (float.IsNaN(value))
|
||||
@@ -143,7 +136,6 @@ namespace ARMeilleure.Instructions
|
||||
value <= ulong.MinValue ? ulong.MinValue : (ulong)value;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static int SatF64ToS32(double value)
|
||||
{
|
||||
if (double.IsNaN(value))
|
||||
@@ -155,7 +147,6 @@ namespace ARMeilleure.Instructions
|
||||
value <= int.MinValue ? int.MinValue : (int)value;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static long SatF64ToS64(double value)
|
||||
{
|
||||
if (double.IsNaN(value))
|
||||
@@ -167,7 +158,6 @@ namespace ARMeilleure.Instructions
|
||||
value <= long.MinValue ? long.MinValue : (long)value;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static uint SatF64ToU32(double value)
|
||||
{
|
||||
if (double.IsNaN(value))
|
||||
@@ -179,7 +169,6 @@ namespace ARMeilleure.Instructions
|
||||
value <= uint.MinValue ? uint.MinValue : (uint)value;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static ulong SatF64ToU64(double value)
|
||||
{
|
||||
if (double.IsNaN(value))
|
||||
@@ -193,7 +182,6 @@ 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;
|
||||
@@ -213,7 +201,6 @@ 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)
|
||||
@@ -237,49 +224,41 @@ 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);
|
||||
@@ -321,22 +300,14 @@ 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)
|
||||
@@ -387,25 +358,21 @@ 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);
|
||||
@@ -413,7 +380,6 @@ 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++)
|
||||
@@ -434,13 +400,11 @@ 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++)
|
||||
@@ -461,7 +425,6 @@ 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++)
|
||||
@@ -482,7 +445,6 @@ 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);
|
||||
@@ -493,7 +455,6 @@ 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);
|
||||
@@ -538,19 +499,16 @@ 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();
|
||||
@@ -569,7 +527,6 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static V128 Sha256SchedulePart2(V128 w0_3, V128 w8_11, V128 w12_15)
|
||||
{
|
||||
V128 result = new();
|
||||
@@ -671,7 +628,6 @@ namespace ARMeilleure.Instructions
|
||||
}
|
||||
#endregion
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static V128 PolynomialMult64_128(ulong op1, ulong op2)
|
||||
{
|
||||
V128 result = V128.Zero;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using ARMeilleure.State;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
@@ -313,7 +312,6 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
static class SoftFloat16_32
|
||||
{
|
||||
[UnmanagedCallersOnly]
|
||||
public static float FPConvert(ushort valueBits)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
@@ -489,7 +487,6 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
static class SoftFloat16_64
|
||||
{
|
||||
[UnmanagedCallersOnly]
|
||||
public static double FPConvert(ushort valueBits)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
@@ -665,7 +662,6 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
static class SoftFloat32_16
|
||||
{
|
||||
[UnmanagedCallersOnly]
|
||||
public static ushort FPConvert(float value)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
@@ -785,19 +781,12 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
static class SoftFloat32
|
||||
{
|
||||
[UnmanagedCallersOnly]
|
||||
public static float FPAdd(float value1, float value2)
|
||||
{
|
||||
return FPAddFpscrImpl(value1, value2, false);
|
||||
return FPAddFpscr(value1, value2, false);
|
||||
}
|
||||
|
||||
[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)
|
||||
public static float FPAddFpscr(float value1, float value2, bool standardFpscr)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
|
||||
@@ -848,8 +837,7 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static int FPCompare(float value1, float value2, byte signalNaNs)
|
||||
public static int FPCompare(float value1, float value2, bool signalNaNs)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
FPCR fpcr = context.Fpcr;
|
||||
@@ -863,7 +851,7 @@ namespace ARMeilleure.Instructions
|
||||
{
|
||||
result = 0b0011;
|
||||
|
||||
if (type1 == FPType.SNaN || type2 == FPType.SNaN || signalNaNs == 1)
|
||||
if (type1 == FPType.SNaN || type2 == FPType.SNaN || signalNaNs)
|
||||
{
|
||||
SoftFloat.FPProcessException(FPException.InvalidOp, context, fpcr);
|
||||
}
|
||||
@@ -887,13 +875,12 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static float FPCompareEQ(float value1, float value2)
|
||||
{
|
||||
return FPCompareEQFpscrImpl(value1, value2, false);
|
||||
return FPCompareEQFpscr(value1, value2, false);
|
||||
}
|
||||
|
||||
private static float FPCompareEQFpscrImpl(float value1, float value2, bool standardFpscr)
|
||||
public static float FPCompareEQFpscr(float value1, float value2, bool standardFpscr)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
|
||||
@@ -920,25 +907,12 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static float FPCompareEQFpscr(float value1, float value2, byte standardFpscr)
|
||||
{
|
||||
return FPCompareEQFpscrImpl(value1, value2, standardFpscr == 1);
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static float FPCompareGE(float value1, float value2)
|
||||
{
|
||||
return FPCompareGEFpscrImpl(value1, value2, false);
|
||||
return FPCompareGEFpscr(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)
|
||||
public static float FPCompareGEFpscr(float value1, float value2, bool standardFpscr)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
|
||||
@@ -962,19 +936,12 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static float FPCompareGT(float value1, float value2)
|
||||
{
|
||||
return FPCompareGTFpscrImpl(value1, value2, false);
|
||||
return FPCompareGTFpscr(value1, value2, false);
|
||||
}
|
||||
|
||||
[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)
|
||||
public static float FPCompareGTFpscr(float value1, float value2, bool standardFpscr)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
|
||||
@@ -998,31 +965,26 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static float FPCompareLE(float value1, float value2)
|
||||
{
|
||||
return FPCompareGEFpscrImpl(value2, value1, false);
|
||||
return FPCompareGE(value2, value1);
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static float FPCompareLT(float value1, float value2)
|
||||
{
|
||||
return FPCompareGTFpscrImpl(value2, value1, false);
|
||||
return FPCompareGT(value2, value1);
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static float FPCompareLEFpscr(float value1, float value2, byte standardFpscr)
|
||||
public static float FPCompareLEFpscr(float value1, float value2, bool standardFpscr)
|
||||
{
|
||||
return FPCompareGEFpscrImpl(value2, value1, standardFpscr == 1);
|
||||
return FPCompareGEFpscr(value2, value1, standardFpscr);
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static float FPCompareLTFpscr(float value1, float value2, byte standardFpscr)
|
||||
public static float FPCompareLTFpscr(float value1, float value2, bool standardFpscr)
|
||||
{
|
||||
return FPCompareGEFpscrImpl(value2, value1, standardFpscr == 1);
|
||||
return FPCompareGTFpscr(value2, value1, standardFpscr);
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static float FPDiv(float value1, float value2)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
@@ -1075,19 +1037,12 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static float FPMax(float value1, float value2)
|
||||
{
|
||||
return FPMaxFpscrImpl(value1, value2, false);
|
||||
return FPMaxFpscr(value1, value2, false);
|
||||
}
|
||||
|
||||
[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)
|
||||
public static float FPMaxFpscr(float value1, float value2, bool standardFpscr)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
|
||||
@@ -1148,13 +1103,12 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static float FPMaxNum(float value1, float value2)
|
||||
{
|
||||
return FPMaxNumFpscrImpl(value1, value2, false);
|
||||
return FPMaxNumFpscr(value1, value2, false);
|
||||
}
|
||||
|
||||
private static float FPMaxNumFpscrImpl(float value1, float value2, bool standardFpscr)
|
||||
public static float FPMaxNumFpscr(float value1, float value2, bool standardFpscr)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
|
||||
@@ -1171,28 +1125,15 @@ namespace ARMeilleure.Instructions
|
||||
value2 = FPInfinity(true);
|
||||
}
|
||||
|
||||
return FPMaxFpscrImpl(value1, value2, standardFpscr);
|
||||
return FPMaxFpscr(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 FPMinFpscrImpl(value1, value2, false);
|
||||
return FPMinFpscr(value1, value2, false);
|
||||
}
|
||||
|
||||
[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)
|
||||
public static float FPMinFpscr(float value1, float value2, bool standardFpscr)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
|
||||
@@ -1253,19 +1194,12 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static float FPMinNum(float value1, float value2)
|
||||
{
|
||||
return FPMinNumFpscrImpl(value1, value2, false);
|
||||
return FPMinNumFpscr(value1, value2, false);
|
||||
}
|
||||
|
||||
[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)
|
||||
public static float FPMinNumFpscr(float value1, float value2, bool standardFpscr)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
|
||||
@@ -1282,22 +1216,15 @@ namespace ARMeilleure.Instructions
|
||||
value2 = FPInfinity(false);
|
||||
}
|
||||
|
||||
return FPMinFpscrImpl(value1, value2, standardFpscr);
|
||||
return FPMinFpscr(value1, value2, standardFpscr);
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static float FPMul(float value1, float value2)
|
||||
{
|
||||
return FPMulFpscrImpl(value1, value2, false);
|
||||
return FPMulFpscr(value1, value2, false);
|
||||
}
|
||||
|
||||
[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)
|
||||
public static float FPMulFpscr(float value1, float value2, bool standardFpscr)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
|
||||
@@ -1344,19 +1271,12 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static float FPMulAdd(float valueA, float value1, float value2)
|
||||
{
|
||||
return FPMulAddFpscrImpl(valueA, value1, value2, false);
|
||||
return FPMulAddFpscr(valueA, value1, value2, false);
|
||||
}
|
||||
|
||||
[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)
|
||||
public static float FPMulAddFpscr(float valueA, float value1, float value2, bool standardFpscr)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
|
||||
@@ -1422,23 +1342,20 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static float FPMulSub(float valueA, float value1, float value2)
|
||||
{
|
||||
value1 = value1.FPNeg();
|
||||
|
||||
return FPMulAddFpscrImpl(valueA, value1, value2, false);
|
||||
return FPMulAdd(valueA, value1, value2);
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static float FPMulSubFpscr(float valueA, float value1, float value2, byte standardFpscr)
|
||||
public static float FPMulSubFpscr(float valueA, float value1, float value2, bool standardFpscr)
|
||||
{
|
||||
value1 = value1.FPNeg();
|
||||
|
||||
return FPMulAddFpscrImpl(valueA, value1, value2, standardFpscr == 1);
|
||||
return FPMulAddFpscr(valueA, value1, value2, standardFpscr);
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static float FPMulX(float value1, float value2)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
@@ -1484,36 +1401,27 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static float FPNegMulAdd(float valueA, float value1, float value2)
|
||||
{
|
||||
valueA = valueA.FPNeg();
|
||||
value1 = value1.FPNeg();
|
||||
|
||||
return FPMulAddFpscrImpl(valueA, value1, value2, false);
|
||||
return FPMulAdd(valueA, value1, value2);
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static float FPNegMulSub(float valueA, float value1, float value2)
|
||||
{
|
||||
valueA = valueA.FPNeg();
|
||||
|
||||
return FPMulAddFpscrImpl(valueA, value1, value2, false);
|
||||
return FPMulAdd(valueA, value1, value2);
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static float FPRecipEstimate(float value)
|
||||
{
|
||||
return FPRecipEstimateFpscrImpl(value, false);
|
||||
return FPRecipEstimateFpscr(value, false);
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static float FPRecipEstimateFpscr(float value, byte standardFpscr)
|
||||
{
|
||||
return FPRecipEstimateFpscrImpl(value, standardFpscr == 1);
|
||||
}
|
||||
|
||||
private static float FPRecipEstimateFpscrImpl(float value, bool standardFpscr)
|
||||
public static float FPRecipEstimateFpscr(float value, bool standardFpscr)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
|
||||
@@ -1600,7 +1508,6 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static float FPRecipStep(float value1, float value2)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
@@ -1626,16 +1533,15 @@ namespace ARMeilleure.Instructions
|
||||
}
|
||||
else
|
||||
{
|
||||
product = FPMulFpscrImpl(value1, value2, true);
|
||||
product = FPMulFpscr(value1, value2, true);
|
||||
}
|
||||
|
||||
result = FPSubFpscrImpl(FPTwo(false), product, true);
|
||||
result = FPSubFpscr(FPTwo(false), product, true);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static float FPRecipStepFused(float value1, float value2)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
@@ -1679,7 +1585,6 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static float FPRecpX(float value)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
@@ -1705,19 +1610,12 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static float FPRSqrtEstimate(float value)
|
||||
{
|
||||
return FPRSqrtEstimateFpscrImpl(value, false);
|
||||
return FPRSqrtEstimateFpscr(value, false);
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static float FPRSqrtEstimateFpscr(float value, byte standardFpscr)
|
||||
{
|
||||
return FPRSqrtEstimateFpscrImpl(value, standardFpscr == 1);
|
||||
}
|
||||
|
||||
private static float FPRSqrtEstimateFpscrImpl(float value, bool standardFpscr)
|
||||
public static float FPRSqrtEstimateFpscr(float value, bool standardFpscr)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
|
||||
@@ -1831,7 +1729,6 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static float FPRSqrtStep(float value1, float value2)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
@@ -1857,7 +1754,7 @@ namespace ARMeilleure.Instructions
|
||||
}
|
||||
else
|
||||
{
|
||||
product = FPMulFpscrImpl(value1, value2, true);
|
||||
product = FPMulFpscr(value1, value2, true);
|
||||
}
|
||||
|
||||
result = FPHalvedSub(FPThree(false), product, context, fpcr);
|
||||
@@ -1866,7 +1763,6 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static float FPRSqrtStepFused(float value1, float value2)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
@@ -1910,7 +1806,6 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static float FPSqrt(float value)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
@@ -1953,13 +1848,12 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static float FPSub(float value1, float value2)
|
||||
{
|
||||
return FPSubFpscrImpl(value1, value2, false);
|
||||
return FPSubFpscr(value1, value2, false);
|
||||
}
|
||||
|
||||
private static float FPSubFpscrImpl(float value1, float value2, bool standardFpscr)
|
||||
public static float FPSubFpscr(float value1, float value2, bool standardFpscr)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
|
||||
@@ -2200,7 +2094,6 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
static class SoftFloat64_16
|
||||
{
|
||||
[UnmanagedCallersOnly]
|
||||
public static ushort FPConvert(double value)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
@@ -2320,19 +2213,12 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
static class SoftFloat64
|
||||
{
|
||||
[UnmanagedCallersOnly]
|
||||
public static double FPAdd(double value1, double value2)
|
||||
{
|
||||
return FPAddFpscrImpl(value1, value2, false);
|
||||
return FPAddFpscr(value1, value2, false);
|
||||
}
|
||||
|
||||
[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)
|
||||
public static double FPAddFpscr(double value1, double value2, bool standardFpscr)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
|
||||
@@ -2383,8 +2269,7 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static int FPCompare(double value1, double value2, byte signalNaNs)
|
||||
public static int FPCompare(double value1, double value2, bool signalNaNs)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
FPCR fpcr = context.Fpcr;
|
||||
@@ -2398,7 +2283,7 @@ namespace ARMeilleure.Instructions
|
||||
{
|
||||
result = 0b0011;
|
||||
|
||||
if (type1 == FPType.SNaN || type2 == FPType.SNaN || signalNaNs == 1)
|
||||
if (type1 == FPType.SNaN || type2 == FPType.SNaN || signalNaNs)
|
||||
{
|
||||
SoftFloat.FPProcessException(FPException.InvalidOp, context, fpcr);
|
||||
}
|
||||
@@ -2422,19 +2307,12 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static double FPCompareEQ(double value1, double value2)
|
||||
{
|
||||
return FPCompareEQFpscrImpl(value1, value2, false);
|
||||
return FPCompareEQFpscr(value1, value2, false);
|
||||
}
|
||||
|
||||
[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)
|
||||
public static double FPCompareEQFpscr(double value1, double value2, bool standardFpscr)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
|
||||
@@ -2461,19 +2339,12 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static double FPCompareGE(double value1, double value2)
|
||||
{
|
||||
return FPCompareGEFpscrImpl(value1, value2, false);
|
||||
return FPCompareGEFpscr(value1, value2, false);
|
||||
}
|
||||
|
||||
[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)
|
||||
public static double FPCompareGEFpscr(double value1, double value2, bool standardFpscr)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
|
||||
@@ -2497,19 +2368,12 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static double FPCompareGT(double value1, double value2)
|
||||
{
|
||||
return FPCompareGTFpscrImpl(value1, value2, false);
|
||||
return FPCompareGTFpscr(value1, value2, false);
|
||||
}
|
||||
|
||||
[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)
|
||||
public static double FPCompareGTFpscr(double value1, double value2, bool standardFpscr)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
|
||||
@@ -2533,31 +2397,26 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static double FPCompareLE(double value1, double value2)
|
||||
{
|
||||
return FPCompareGEFpscrImpl(value2, value1, false);
|
||||
return FPCompareGE(value2, value1);
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static double FPCompareLT(double value1, double value2)
|
||||
{
|
||||
return FPCompareGTFpscrImpl(value2, value1, false);
|
||||
return FPCompareGT(value2, value1);
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static double FPCompareLEFpscr(double value1, double value2, byte standardFpscr)
|
||||
public static double FPCompareLEFpscr(double value1, double value2, bool standardFpscr)
|
||||
{
|
||||
return FPCompareGEFpscrImpl(value2, value1, standardFpscr == 1);
|
||||
return FPCompareGEFpscr(value2, value1, standardFpscr);
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static double FPCompareLTFpscr(double value1, double value2, byte standardFpscr)
|
||||
public static double FPCompareLTFpscr(double value1, double value2, bool standardFpscr)
|
||||
{
|
||||
return FPCompareGTFpscrImpl(value2, value1, standardFpscr == 1);
|
||||
return FPCompareGTFpscr(value2, value1, standardFpscr);
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static double FPDiv(double value1, double value2)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
@@ -2610,19 +2469,12 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static double FPMax(double value1, double value2)
|
||||
{
|
||||
return FPMaxFpscrImpl(value1, value2, false);
|
||||
return FPMaxFpscr(value1, value2, false);
|
||||
}
|
||||
|
||||
[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)
|
||||
public static double FPMaxFpscr(double value1, double value2, bool standardFpscr)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
|
||||
@@ -2683,19 +2535,12 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static double FPMaxNum(double value1, double value2)
|
||||
{
|
||||
return FPMaxNumFpscrImpl(value1, value2, false);
|
||||
return FPMaxNumFpscr(value1, value2, false);
|
||||
}
|
||||
|
||||
[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)
|
||||
public static double FPMaxNumFpscr(double value1, double value2, bool standardFpscr)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
|
||||
@@ -2712,22 +2557,15 @@ namespace ARMeilleure.Instructions
|
||||
value2 = FPInfinity(true);
|
||||
}
|
||||
|
||||
return FPMaxFpscrImpl(value1, value2, standardFpscr);
|
||||
return FPMaxFpscr(value1, value2, standardFpscr);
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static double FPMin(double value1, double value2)
|
||||
{
|
||||
return FPMinFpscrImpl(value1, value2, false);
|
||||
return FPMinFpscr(value1, value2, false);
|
||||
}
|
||||
|
||||
[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)
|
||||
public static double FPMinFpscr(double value1, double value2, bool standardFpscr)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
|
||||
@@ -2788,19 +2626,12 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static double FPMinNum(double value1, double value2)
|
||||
{
|
||||
return FPMinNumFpscrImpl(value1, value2, false);
|
||||
return FPMinNumFpscr(value1, value2, false);
|
||||
}
|
||||
|
||||
[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)
|
||||
public static double FPMinNumFpscr(double value1, double value2, bool standardFpscr)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
|
||||
@@ -2817,22 +2648,15 @@ namespace ARMeilleure.Instructions
|
||||
value2 = FPInfinity(false);
|
||||
}
|
||||
|
||||
return FPMinFpscrImpl(value1, value2, standardFpscr);
|
||||
return FPMinFpscr(value1, value2, standardFpscr);
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static double FPMul(double value1, double value2)
|
||||
{
|
||||
return FPMulFpscrImpl(value1, value2, false);
|
||||
return FPMulFpscr(value1, value2, false);
|
||||
}
|
||||
|
||||
[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)
|
||||
public static double FPMulFpscr(double value1, double value2, bool standardFpscr)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
|
||||
@@ -2879,19 +2703,12 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static double FPMulAdd(double valueA, double value1, double value2)
|
||||
{
|
||||
return FPMulAddFpscrImpl(valueA, value1, value2, false);
|
||||
return FPMulAddFpscr(valueA, value1, value2, false);
|
||||
}
|
||||
|
||||
[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)
|
||||
public static double FPMulAddFpscr(double valueA, double value1, double value2, bool standardFpscr)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
|
||||
@@ -2957,23 +2774,20 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static double FPMulSub(double valueA, double value1, double value2)
|
||||
{
|
||||
value1 = value1.FPNeg();
|
||||
|
||||
return FPMulAddFpscrImpl(valueA, value1, value2, false);
|
||||
return FPMulAdd(valueA, value1, value2);
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static double FPMulSubFpscr(double valueA, double value1, double value2, byte standardFpscr)
|
||||
public static double FPMulSubFpscr(double valueA, double value1, double value2, bool standardFpscr)
|
||||
{
|
||||
value1 = value1.FPNeg();
|
||||
|
||||
return FPMulAddFpscrImpl(valueA, value1, value2, standardFpscr == 1);
|
||||
return FPMulAddFpscr(valueA, value1, value2, standardFpscr);
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static double FPMulX(double value1, double value2)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
@@ -3019,36 +2833,27 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static double FPNegMulAdd(double valueA, double value1, double value2)
|
||||
{
|
||||
valueA = valueA.FPNeg();
|
||||
value1 = value1.FPNeg();
|
||||
|
||||
return FPMulAddFpscrImpl(valueA, value1, value2, false);
|
||||
return FPMulAdd(valueA, value1, value2);
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static double FPNegMulSub(double valueA, double value1, double value2)
|
||||
{
|
||||
valueA = valueA.FPNeg();
|
||||
|
||||
return FPMulAddFpscrImpl(valueA, value1, value2, false);
|
||||
return FPMulAdd(valueA, value1, value2);
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static double FPRecipEstimate(double value)
|
||||
{
|
||||
return FPRecipEstimateFpscrImpl(value, false);
|
||||
return FPRecipEstimateFpscr(value, false);
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static double FPRecipEstimateFpscr(double value, byte standardFpscr)
|
||||
{
|
||||
return FPRecipEstimateFpscrImpl(value, standardFpscr == 1);
|
||||
}
|
||||
|
||||
private static double FPRecipEstimateFpscrImpl(double value, bool standardFpscr)
|
||||
public static double FPRecipEstimateFpscr(double value, bool standardFpscr)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
|
||||
@@ -3135,7 +2940,6 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static double FPRecipStep(double value1, double value2)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
@@ -3161,7 +2965,7 @@ namespace ARMeilleure.Instructions
|
||||
}
|
||||
else
|
||||
{
|
||||
product = FPMulFpscrImpl(value1, value2, true);
|
||||
product = FPMulFpscr(value1, value2, true);
|
||||
}
|
||||
|
||||
result = FPSubFpscr(FPTwo(false), product, true);
|
||||
@@ -3170,7 +2974,6 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static double FPRecipStepFused(double value1, double value2)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
@@ -3214,7 +3017,6 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static double FPRecpX(double value)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
@@ -3240,19 +3042,12 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static double FPRSqrtEstimate(double value)
|
||||
{
|
||||
return FPRSqrtEstimateFpscrImpl(value, false);
|
||||
return FPRSqrtEstimateFpscr(value, false);
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static double FPRSqrtEstimateFpscr(double value, byte standardFpscr)
|
||||
{
|
||||
return FPRSqrtEstimateFpscrImpl(value, standardFpscr == 1);
|
||||
}
|
||||
|
||||
private static double FPRSqrtEstimateFpscrImpl(double value, bool standardFpscr)
|
||||
public static double FPRSqrtEstimateFpscr(double value, bool standardFpscr)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
|
||||
@@ -3366,7 +3161,6 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static double FPRSqrtStep(double value1, double value2)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
@@ -3392,7 +3186,7 @@ namespace ARMeilleure.Instructions
|
||||
}
|
||||
else
|
||||
{
|
||||
product = FPMulFpscrImpl(value1, value2, true);
|
||||
product = FPMulFpscr(value1, value2, true);
|
||||
}
|
||||
|
||||
result = FPHalvedSub(FPThree(false), product, context, fpcr);
|
||||
@@ -3401,7 +3195,6 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static double FPRSqrtStepFused(double value1, double value2)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
@@ -3445,7 +3238,6 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static double FPSqrt(double value)
|
||||
{
|
||||
ExecutionContext context = NativeInterface.GetContext();
|
||||
@@ -3488,7 +3280,6 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
public static double FPSub(double value1, double value2)
|
||||
{
|
||||
return FPSubFpscr(value1, value2, false);
|
||||
|
||||
@@ -7,6 +7,7 @@ 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;
|
||||
|
||||
@@ -21,6 +22,7 @@ namespace ARMeilleure.Memory
|
||||
granularity = DefaultGranularity;
|
||||
}
|
||||
|
||||
Allocator = allocator;
|
||||
Block = allocator.Reserve(maxSize);
|
||||
_maxSize = maxSize;
|
||||
_sizeGranularity = granularity;
|
||||
|
||||
@@ -2,6 +2,8 @@ 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;
|
||||
@@ -18,51 +20,68 @@ namespace ARMeilleure.Translation.Cache
|
||||
private static readonly int _pageMask = _pageSize - 1;
|
||||
|
||||
private const int CodeAlignment = 4; // Bytes.
|
||||
private const int CacheSize = 2047 * 1024 * 1024;
|
||||
private const int CacheSize = 256 * 1024 * 1024;
|
||||
|
||||
private static ReservedRegion _jitRegion;
|
||||
private static JitCacheInvalidation _jitCacheInvalidator;
|
||||
|
||||
private static CacheMemoryAllocator _cacheAllocator;
|
||||
private static List<CacheMemoryAllocator> _cacheAllocators = [];
|
||||
|
||||
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)
|
||||
{
|
||||
return;
|
||||
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;
|
||||
}
|
||||
|
||||
_jitRegion = new ReservedRegion(allocator, CacheSize);
|
||||
_activeRegionIndex = 0;
|
||||
|
||||
ReservedRegion firstRegion = new(allocator, CacheSize);
|
||||
_jitRegions.Add(firstRegion);
|
||||
|
||||
CacheMemoryAllocator firstCacheAllocator = new(CacheSize);
|
||||
_cacheAllocators.Add(firstCacheAllocator);
|
||||
|
||||
if (!OperatingSystem.IsWindows() && !OperatingSystem.IsMacOS())
|
||||
{
|
||||
_jitCacheInvalidator = new JitCacheInvalidation(allocator);
|
||||
}
|
||||
|
||||
_cacheAllocator = new CacheMemoryAllocator(CacheSize);
|
||||
|
||||
if (OperatingSystem.IsWindows())
|
||||
{
|
||||
JitUnwindWindows.InstallFunctionTableHandler(_jitRegion.Pointer, CacheSize, _jitRegion.Pointer + Allocate(_pageSize));
|
||||
JitUnwindWindows.InstallFunctionTableHandler(
|
||||
firstRegion.Pointer, CacheSize, firstRegion.Pointer + Allocate(_pageSize)
|
||||
);
|
||||
}
|
||||
|
||||
_initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,8 +94,8 @@ namespace ARMeilleure.Translation.Cache
|
||||
Debug.Assert(_initialized);
|
||||
|
||||
int funcOffset = Allocate(code.Length);
|
||||
|
||||
nint funcPtr = _jitRegion.Pointer + funcOffset;
|
||||
ReservedRegion targetRegion = _jitRegions[_activeRegionIndex];
|
||||
nint funcPtr = targetRegion.Pointer + funcOffset;
|
||||
|
||||
if (OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
|
||||
{
|
||||
@@ -90,9 +109,9 @@ namespace ARMeilleure.Translation.Cache
|
||||
}
|
||||
else
|
||||
{
|
||||
ReprotectAsWritable(funcOffset, code.Length);
|
||||
ReprotectAsWritable(targetRegion, funcOffset, code.Length);
|
||||
Marshal.Copy(code, 0, funcPtr, code.Length);
|
||||
ReprotectAsExecutable(funcOffset, code.Length);
|
||||
ReprotectAsExecutable(targetRegion, funcOffset, code.Length);
|
||||
|
||||
if (OperatingSystem.IsWindows() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
|
||||
{
|
||||
@@ -116,52 +135,77 @@ namespace ARMeilleure.Translation.Cache
|
||||
{
|
||||
Debug.Assert(_initialized);
|
||||
|
||||
int funcOffset = (int)(pointer.ToInt64() - _jitRegion.Pointer.ToInt64());
|
||||
|
||||
if (TryFind(funcOffset, out CacheEntry entry, out int entryIndex) && entry.Offset == funcOffset)
|
||||
foreach (ReservedRegion region in _jitRegions)
|
||||
{
|
||||
_cacheAllocator.Free(funcOffset, AlignCodeSize(entry.Size));
|
||||
_cacheEntries.RemoveAt(entryIndex);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void ReprotectAsWritable(int offset, int size)
|
||||
private static void ReprotectAsWritable(ReservedRegion region, int offset, int size)
|
||||
{
|
||||
int endOffs = offset + size;
|
||||
|
||||
int regionStart = offset & ~_pageMask;
|
||||
int regionEnd = (endOffs + _pageMask) & ~_pageMask;
|
||||
|
||||
_jitRegion.Block.MapAsRwx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
||||
region.Block.MapAsRwx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
||||
}
|
||||
|
||||
private static void ReprotectAsExecutable(int offset, int size)
|
||||
private static void ReprotectAsExecutable(ReservedRegion region, int offset, int size)
|
||||
{
|
||||
int endOffs = offset + size;
|
||||
|
||||
int regionStart = offset & ~_pageMask;
|
||||
int regionEnd = (endOffs + _pageMask) & ~_pageMask;
|
||||
|
||||
_jitRegion.Block.MapAsRx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
||||
region.Block.MapAsRx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
||||
}
|
||||
|
||||
private static int Allocate(int codeSize)
|
||||
{
|
||||
codeSize = AlignCodeSize(codeSize);
|
||||
|
||||
int allocOffset = _cacheAllocator.Allocate(codeSize);
|
||||
int allocOffset = _cacheAllocators[_activeRegionIndex].Allocate(codeSize);
|
||||
|
||||
if (allocOffset < 0)
|
||||
if (allocOffset >= 0)
|
||||
{
|
||||
throw new OutOfMemoryException("JIT Cache exhausted.");
|
||||
_jitRegions[_activeRegionIndex].ExpandIfNeeded((ulong)allocOffset + (ulong)codeSize);
|
||||
return allocOffset;
|
||||
}
|
||||
|
||||
_jitRegion.ExpandIfNeeded((ulong)allocOffset + (ulong)codeSize);
|
||||
int exhaustedRegion = _activeRegionIndex;
|
||||
ReservedRegion newRegion = new(_jitRegions[0].Allocator, CacheSize);
|
||||
_jitRegions.Add(newRegion);
|
||||
_activeRegionIndex = _jitRegions.Count - 1;
|
||||
|
||||
return allocOffset;
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
private static int AlignCodeSize(int codeSize)
|
||||
{
|
||||
return checked(codeSize + (CodeAlignment - 1)) & ~(CodeAlignment - 1);
|
||||
@@ -185,18 +229,21 @@ namespace ARMeilleure.Translation.Cache
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
int index = _cacheEntries.BinarySearch(new CacheEntry(offset, 0, default));
|
||||
|
||||
if (index < 0)
|
||||
foreach (ReservedRegion _ in _jitRegions)
|
||||
{
|
||||
index = ~index - 1;
|
||||
}
|
||||
int index = _cacheEntries.BinarySearch(new CacheEntry(offset, 0, default));
|
||||
|
||||
if (index >= 0)
|
||||
{
|
||||
entry = _cacheEntries[index];
|
||||
entryIndex = index;
|
||||
return true;
|
||||
if (index < 0)
|
||||
{
|
||||
index = ~index - 1;
|
||||
}
|
||||
|
||||
if (index >= 0)
|
||||
{
|
||||
entry = _cacheEntries[index];
|
||||
entryIndex = index;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -52,6 +52,11 @@ 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;
|
||||
@@ -91,6 +96,23 @@ 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());
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
using System;
|
||||
|
||||
namespace ARMeilleure.Translation
|
||||
{
|
||||
class DelegateInfo
|
||||
{
|
||||
public nint FuncPtr { get; private set; }
|
||||
public DelegateInfo(nint funcPtr)
|
||||
#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)
|
||||
{
|
||||
_dlg = dlg;
|
||||
FuncPtr = funcPtr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
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
|
||||
{
|
||||
@@ -33,6 +36,20 @@ 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);
|
||||
@@ -49,11 +66,11 @@ namespace ARMeilleure.Translation
|
||||
return index;
|
||||
}
|
||||
|
||||
private static void SetDelegateInfo(MethodInfo method)
|
||||
private static void SetDelegateInfo(Delegate dlg, nint funcPtr)
|
||||
{
|
||||
string key = GetKey(method);
|
||||
string key = GetKey(dlg.Method);
|
||||
|
||||
_delegates.Add(key, new DelegateInfo(method.MethodHandle.GetFunctionPointer())); // ArgumentException (key).
|
||||
_delegates.Add(key, new DelegateInfo(dlg, funcPtr)); // ArgumentException (key).
|
||||
}
|
||||
|
||||
private static string GetKey(MethodInfo info)
|
||||
@@ -67,179 +84,533 @@ namespace ARMeilleure.Translation
|
||||
{
|
||||
_delegates = new SortedList<string, DelegateInfo>();
|
||||
|
||||
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)));
|
||||
// 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(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)));
|
||||
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(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)));
|
||||
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(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)));
|
||||
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);
|
||||
|
||||
SetDelegateInfo(typeof(SoftFloat16_32).GetMethod(nameof(SoftFloat16_32.FPConvert)));
|
||||
SetDelegateInfo(typeof(SoftFloat16_64).GetMethod(nameof(SoftFloat16_64.FPConvert)));
|
||||
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(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)));
|
||||
SoftFloat16_32FPConvert dlgSoftFloat16_32FPConvert = new(SoftFloat16_32.FPConvert);
|
||||
SoftFloat16_64FPConvert dlgSoftFloat16_64FPConvert = new(SoftFloat16_64.FPConvert);
|
||||
|
||||
SetDelegateInfo(typeof(SoftFloat32_16).GetMethod(nameof(SoftFloat32_16.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(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)));
|
||||
SoftFloat32_16FPConvert dlgSoftFloat32_16FPConvert = new(SoftFloat32_16.FPConvert);
|
||||
|
||||
SetDelegateInfo(typeof(SoftFloat64_16).GetMethod(nameof(SoftFloat64_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);
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ namespace ARMeilleure.Translation
|
||||
|
||||
public virtual Operand Call(MethodInfo info, params Operand[] callArgs)
|
||||
{
|
||||
nint funcPtr = info.MethodHandle.GetFunctionPointer();
|
||||
nint funcPtr = Delegates.GetDelegateFuncPtr(info);
|
||||
|
||||
OperandType returnType = GetOperandType(info.ReturnType);
|
||||
|
||||
|
||||
@@ -144,17 +144,15 @@ namespace ARMeilleure.Translation.PTC
|
||||
|
||||
public List<ulong> GetBlacklistedFunctions()
|
||||
{
|
||||
List<ulong> funcs = new List<ulong>();
|
||||
List<ulong> funcs = [];
|
||||
|
||||
foreach (var profiledFunc in ProfiledFuncs)
|
||||
foreach ((ulong ptr, FuncProfile funcProfile) in ProfiledFuncs)
|
||||
{
|
||||
if (profiledFunc.Value.Blacklist)
|
||||
{
|
||||
if (!funcs.Contains(profiledFunc.Key))
|
||||
{
|
||||
funcs.Add(profiledFunc.Key);
|
||||
}
|
||||
}
|
||||
if (!funcProfile.Blacklist)
|
||||
continue;
|
||||
|
||||
if (!funcs.Contains(ptr))
|
||||
funcs.Add(ptr);
|
||||
}
|
||||
|
||||
return funcs;
|
||||
|
||||
@@ -457,7 +457,7 @@ namespace ARMeilleure.Translation
|
||||
context.Store(address, count);
|
||||
context.BranchIf(lblEnd, curCount, Const(MinsCallForRejit), Comparison.NotEqual, BasicBlockFrequency.Cold);
|
||||
|
||||
context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.EnqueueForRejit)), Const(context.EntryAddress));
|
||||
context.Call(NativeInterface.Type.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(typeof(NativeInterface).GetMethod(nameof(NativeInterface.CheckSynchronization)));
|
||||
Operand running = context.Call(NativeInterface.Type.GetMethod(nameof(NativeInterface.CheckSynchronization)));
|
||||
context.BranchIfTrue(lblExit, running, BasicBlockFrequency.Cold);
|
||||
|
||||
context.Return(Const(0L));
|
||||
|
||||
@@ -4,6 +4,7 @@ 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;
|
||||
|
||||
@@ -181,7 +182,7 @@ namespace ARMeilleure.Translation
|
||||
context.Tailcall(hostAddress, nativeContext);
|
||||
|
||||
context.MarkLabel(lblFallback);
|
||||
hostAddress = context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFunctionAddress)), guestAddress);
|
||||
hostAddress = context.Call(NativeInterface.Type.GetMethod(nameof(NativeInterface.GetFunctionAddress)), guestAddress);
|
||||
context.Tailcall(hostAddress, nativeContext);
|
||||
|
||||
ControlFlowGraph cfg = context.GetControlFlowGraph();
|
||||
@@ -206,7 +207,7 @@ namespace ARMeilleure.Translation
|
||||
Operand guestAddress = context.Load(OperandType.I64,
|
||||
context.Add(nativeContext, Const((ulong)NativeContext.GetDispatchAddressOffset())));
|
||||
|
||||
Operand hostAddress = context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFunctionAddress)), guestAddress);
|
||||
Operand hostAddress = context.Call(NativeInterface.Type.GetMethod(nameof(NativeInterface.GetFunctionAddress)), guestAddress);
|
||||
context.Tailcall(hostAddress, nativeContext);
|
||||
|
||||
ControlFlowGraph cfg = context.GetControlFlowGraph();
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
<Configurations>Debug;Release;DebugAOT;ReleaseAOT</Configurations>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<PropertyGroup>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
<Configurations>Debug;Release;DebugAOT;ReleaseAOT</Configurations>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<RuntimeIdentifiers>win-x64;osx-x64;linux-x64</RuntimeIdentifiers>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
<Configurations>Debug;Release;DebugAOT;ReleaseAOT</Configurations>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<PropertyGroup>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
<Configurations>Debug;Release;DebugAOT;ReleaseAOT</Configurations>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -6,9 +6,7 @@ namespace Ryujinx.Common.Configuration
|
||||
[JsonConverter(typeof(TypedStringEnumConverter<GraphicsBackend>))]
|
||||
public enum GraphicsBackend
|
||||
{
|
||||
Auto,
|
||||
Vulkan,
|
||||
OpenGl,
|
||||
Metal
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,15 +5,34 @@ 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 => RuntimeInformation.OSArchitecture is Architecture.Arm64;
|
||||
public static bool IsArm => Architecture is Architecture.Arm64;
|
||||
|
||||
public static bool IsX64 => RuntimeInformation.OSArchitecture is Architecture.X64;
|
||||
public static bool IsX64 => Architecture is Architecture.X64;
|
||||
|
||||
public static bool IsIntelMac => IsMacOS && IsX64;
|
||||
public static bool IsArmMac => IsMacOS && IsArm;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DefineConstants Condition=" '$(ExtraDefineConstants)' != '' ">$(DefineConstants);$(ExtraDefineConstants)</DefineConstants>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
<Configurations>Debug;Release;DebugAOT;ReleaseAOT</Configurations>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
9
src/Ryujinx.Common/SharedConstants.cs
Normal file
9
src/Ryujinx.Common/SharedConstants.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
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";
|
||||
}
|
||||
}
|
||||
@@ -10,55 +10,6 @@ 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";
|
||||
|
||||
@@ -164,15 +115,16 @@ 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
|
||||
@@ -218,6 +170,7 @@ 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
|
||||
@@ -228,6 +181,7 @@ 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
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using ARMeilleure.Memory;
|
||||
using Humanizer;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Memory;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -15,9 +17,8 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
||||
private static readonly int _pageMask = _pageSize - 1;
|
||||
|
||||
private const int CodeAlignment = 4; // Bytes.
|
||||
private const int CacheSize = 2047 * 1024 * 1024;
|
||||
private const int CacheSize = 256 * 1024 * 1024;
|
||||
|
||||
private static ReservedRegion _jitRegion;
|
||||
private static JitCacheInvalidation _jitCacheInvalidator;
|
||||
|
||||
private static CacheMemoryAllocator _cacheAllocator;
|
||||
@@ -26,6 +27,8 @@ 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)]
|
||||
@@ -45,7 +48,9 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
||||
return;
|
||||
}
|
||||
|
||||
_jitRegion = new ReservedRegion(allocator, CacheSize);
|
||||
ReservedRegion firstRegion = new(allocator, CacheSize);
|
||||
_jitRegions.Add(firstRegion);
|
||||
_activeRegionIndex = 0;
|
||||
|
||||
if (!OperatingSystem.IsWindows() && !OperatingSystem.IsMacOS())
|
||||
{
|
||||
@@ -65,8 +70,8 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
||||
Debug.Assert(_initialized);
|
||||
|
||||
int funcOffset = Allocate(code.Length);
|
||||
|
||||
nint funcPtr = _jitRegion.Pointer + funcOffset;
|
||||
ReservedRegion targetRegion = _jitRegions[_activeRegionIndex];
|
||||
nint funcPtr = targetRegion.Pointer + funcOffset;
|
||||
|
||||
if (OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
|
||||
{
|
||||
@@ -80,18 +85,11 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
||||
}
|
||||
else
|
||||
{
|
||||
ReprotectAsWritable(funcOffset, code.Length);
|
||||
code.CopyTo(new Span<byte>((void*)funcPtr, code.Length));
|
||||
ReprotectAsExecutable(funcOffset, code.Length);
|
||||
ReprotectAsWritable(targetRegion, funcOffset, code.Length);
|
||||
Marshal.Copy(code.ToArray(), 0, funcPtr, code.Length);
|
||||
ReprotectAsExecutable(targetRegion, funcOffset, code.Length);
|
||||
|
||||
if (OperatingSystem.IsWindows() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
|
||||
{
|
||||
FlushInstructionCache(Process.GetCurrentProcess().Handle, funcPtr, (nuint)code.Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
_jitCacheInvalidator?.Invalidate(funcPtr, (ulong)code.Length);
|
||||
}
|
||||
_jitCacheInvalidator?.Invalidate(funcPtr, (ulong)code.Length);
|
||||
}
|
||||
|
||||
Add(funcOffset, code.Length);
|
||||
@@ -106,50 +104,80 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
||||
{
|
||||
Debug.Assert(_initialized);
|
||||
|
||||
int funcOffset = (int)(pointer.ToInt64() - _jitRegion.Pointer.ToInt64());
|
||||
|
||||
if (TryFind(funcOffset, out CacheEntry entry, out int entryIndex) && entry.Offset == funcOffset)
|
||||
foreach (ReservedRegion region in _jitRegions)
|
||||
{
|
||||
_cacheAllocator.Free(funcOffset, AlignCodeSize(entry.Size));
|
||||
_cacheEntries.RemoveAt(entryIndex);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void ReprotectAsWritable(int offset, int size)
|
||||
private static void ReprotectAsWritable(ReservedRegion region, int offset, int size)
|
||||
{
|
||||
int endOffs = offset + size;
|
||||
|
||||
int regionStart = offset & ~_pageMask;
|
||||
int regionEnd = (endOffs + _pageMask) & ~_pageMask;
|
||||
|
||||
_jitRegion.Block.MapAsRwx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
||||
region.Block.MapAsRwx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
||||
}
|
||||
|
||||
private static void ReprotectAsExecutable(int offset, int size)
|
||||
private static void ReprotectAsExecutable(ReservedRegion region, int offset, int size)
|
||||
{
|
||||
int endOffs = offset + size;
|
||||
|
||||
int regionStart = offset & ~_pageMask;
|
||||
int regionEnd = (endOffs + _pageMask) & ~_pageMask;
|
||||
|
||||
_jitRegion.Block.MapAsRx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
||||
region.Block.MapAsRx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
||||
}
|
||||
|
||||
private static int Allocate(int codeSize)
|
||||
{
|
||||
codeSize = AlignCodeSize(codeSize);
|
||||
|
||||
int allocOffset = _cacheAllocator.Allocate(codeSize);
|
||||
|
||||
if (allocOffset < 0)
|
||||
for (int i = _activeRegionIndex; i < _jitRegions.Count; i++)
|
||||
{
|
||||
throw new OutOfMemoryException("JIT Cache exhausted.");
|
||||
int allocOffset = _cacheAllocator.Allocate(codeSize);
|
||||
|
||||
if (allocOffset >= 0)
|
||||
{
|
||||
_jitRegions[i].ExpandIfNeeded((ulong)allocOffset + (ulong)codeSize);
|
||||
_activeRegionIndex = i;
|
||||
return allocOffset;
|
||||
}
|
||||
}
|
||||
|
||||
_jitRegion.ExpandIfNeeded((ulong)allocOffset + (ulong)codeSize);
|
||||
int exhaustedRegion = _activeRegionIndex;
|
||||
ReservedRegion newRegion = new(_jitRegions[0].Allocator, CacheSize);
|
||||
_jitRegions.Add(newRegion);
|
||||
_activeRegionIndex = _jitRegions.Count - 1;
|
||||
|
||||
return allocOffset;
|
||||
int newRegionNumber = _activeRegionIndex;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
private static int AlignCodeSize(int codeSize)
|
||||
|
||||
@@ -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 = 128 * 1024 * 1024;
|
||||
private const int LocalCacheSize = 256 * 1024 * 1024;
|
||||
|
||||
// How many calls to the same function we allow until we pad the shared cache to force the function to become available there
|
||||
// and allow the guest to take the fast path.
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<PropertyGroup>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
<Configurations>Debug;Release;DebugAOT;ReleaseAOT</Configurations>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
<Configurations>Debug;Release;DebugAOT;ReleaseAOT</Configurations>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -339,84 +339,6 @@ 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>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using Ryujinx.Common.Configuration;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Graphics.GAL.Multithreading;
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
@@ -10,6 +12,20 @@ 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; }
|
||||
|
||||
@@ -2,16 +2,25 @@
|
||||
|
||||
<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" />
|
||||
|
||||
@@ -4,22 +4,23 @@ 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,
|
||||
ComputeSize computeLocalSize,
|
||||
ProgramPipelineState? state,
|
||||
bool fromCache = false)
|
||||
public ShaderInfo(int fragmentOutputMap, ResourceLayout resourceLayout, 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Ryujinx.Graphics.Device;
|
||||
using Ryujinx.Graphics.Gpu.Engine.GPFifo;
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Engine.MME
|
||||
{
|
||||
@@ -52,7 +53,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
|
||||
{
|
||||
_executionEngine = new MacroHLE(processor, _hleFunction);
|
||||
}
|
||||
else if (GraphicsConfig.EnableMacroJit)
|
||||
else if (GraphicsConfig.EnableMacroJit && RuntimeFeature.IsDynamicCodeSupported)
|
||||
{
|
||||
_executionEngine = new MacroJit();
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
|
||||
/// </summary>
|
||||
class VtgAsComputeContext : IDisposable
|
||||
{
|
||||
private const int DummyBufferSize = 16;
|
||||
|
||||
private readonly GpuContext _context;
|
||||
|
||||
/// <summary>
|
||||
@@ -46,7 +48,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
format.GetBytesPerElement(),
|
||||
1,
|
||||
format,
|
||||
DepthStencilMode.Depth,
|
||||
Target.TextureBuffer,
|
||||
@@ -519,6 +521,21 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
|
||||
return new BufferRange(_geometryIndexDataBuffer.Handle, offset, size, write);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the range for a dummy 16 bytes buffer, filled with zeros.
|
||||
/// </summary>
|
||||
/// <returns>Dummy buffer range</returns>
|
||||
public BufferRange GetDummyBufferRange()
|
||||
{
|
||||
if (_dummyBuffer == BufferHandle.Null)
|
||||
{
|
||||
_dummyBuffer = _context.Renderer.CreateBuffer(DummyBufferSize, BufferAccess.DeviceMemory);
|
||||
_context.Renderer.Pipeline.ClearBuffer(_dummyBuffer, 0, DummyBufferSize, 0);
|
||||
}
|
||||
|
||||
return new BufferRange(_dummyBuffer, 0, DummyBufferSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the range for a sequential index buffer, with ever incrementing index values.
|
||||
/// </summary>
|
||||
|
||||
@@ -147,6 +147,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
|
||||
{
|
||||
_vacContext.VertexInfoBufferUpdater.SetVertexStride(index, 0, componentsCount);
|
||||
_vacContext.VertexInfoBufferUpdater.SetVertexOffset(index, 0, 0);
|
||||
SetDummyBufferTexture(_vertexAsCompute.Reservations, index, format);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -162,12 +163,15 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
|
||||
{
|
||||
_vacContext.VertexInfoBufferUpdater.SetVertexStride(index, 0, componentsCount);
|
||||
_vacContext.VertexInfoBufferUpdater.SetVertexOffset(index, 0, 0);
|
||||
SetDummyBufferTexture(_vertexAsCompute.Reservations, index, format);
|
||||
continue;
|
||||
}
|
||||
|
||||
int vbStride = vertexBuffer.UnpackStride();
|
||||
ulong vbSize = GetVertexBufferSize(address, endAddress.Pack(), vbStride, _indexed, instanced, _firstVertex, _count);
|
||||
|
||||
ulong oldVbSize = vbSize;
|
||||
|
||||
ulong attributeOffset = (ulong)vertexAttrib.UnpackOffset();
|
||||
int componentSize = format.GetScalarSize();
|
||||
|
||||
@@ -340,6 +344,20 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
|
||||
return maxOutputVertices / verticesPerPrimitive;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Binds a dummy buffer as vertex buffer into a buffer texture.
|
||||
/// </summary>
|
||||
/// <param name="reservations">Shader resource binding reservations</param>
|
||||
/// <param name="index">Buffer texture index</param>
|
||||
/// <param name="format">Buffer texture format</param>
|
||||
private readonly void SetDummyBufferTexture(ResourceReservations reservations, int index, Format format)
|
||||
{
|
||||
ITexture bufferTexture = _vacContext.EnsureBufferTexture(index + 2, format);
|
||||
bufferTexture.SetStorage(_vacContext.GetDummyBufferRange());
|
||||
|
||||
_context.Renderer.Pipeline.SetTextureAndSampler(ShaderStage.Compute, reservations.GetVertexBufferTextureBinding(index), bufferTexture, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Binds a vertex buffer into a buffer texture.
|
||||
/// </summary>
|
||||
|
||||
@@ -913,7 +913,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
|
||||
Span<Rectangle<int>> scissors =
|
||||
[
|
||||
new Rectangle<int>(scissorX, scissorY, scissorW, scissorH)
|
||||
new(scissorX, scissorY, scissorW, scissorH)
|
||||
];
|
||||
|
||||
_context.Renderer.Pipeline.SetScissors(scissors);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<PropertyGroup>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
<Configurations>Debug;Release;DebugAOT;ReleaseAOT</Configurations>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -324,11 +324,6 @@ 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();
|
||||
@@ -397,8 +392,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
||||
context,
|
||||
shaders,
|
||||
specState.PipelineState,
|
||||
specState.TransformFeedbackDescriptors != null,
|
||||
specState.ComputeState.GetLocalSize());
|
||||
specState.TransformFeedbackDescriptors != null);
|
||||
|
||||
IProgram hostProgram;
|
||||
|
||||
@@ -635,10 +629,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
||||
return;
|
||||
}
|
||||
|
||||
if (context.Capabilities.Api != TargetApi.Metal)
|
||||
{
|
||||
WriteHostCode(context, hostCode, program.Shaders, streams, timestamp);
|
||||
}
|
||||
WriteHostCode(context, hostCode, program.Shaders, streams, timestamp);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using Ryujinx.Common.Configuration;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.Shader;
|
||||
@@ -367,9 +366,6 @@ 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);
|
||||
}
|
||||
@@ -494,12 +490,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
||||
{
|
||||
ShaderSource[] shaderSources = new ShaderSource[compilation.TranslatedStages.Length];
|
||||
|
||||
ref GpuChannelComputeState computeState = ref compilation.SpecializationState.ComputeState;
|
||||
|
||||
ShaderInfoBuilder shaderInfoBuilder = new(
|
||||
_context,
|
||||
compilation.SpecializationState.TransformFeedbackDescriptors != null,
|
||||
computeLocalSize: computeState.GetLocalSize());
|
||||
ShaderInfoBuilder shaderInfoBuilder = new(_context, compilation.SpecializationState.TransformFeedbackDescriptors != null);
|
||||
|
||||
for (int index = 0; index < compilation.TranslatedStages.Length; index++)
|
||||
{
|
||||
@@ -729,7 +720,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
||||
|
||||
ShaderProgram program = translatorContext.Translate();
|
||||
|
||||
CachedShaderStage[] shaders = [new CachedShaderStage(program.Info, shader.Code, shader.Cb1Data)];
|
||||
CachedShaderStage[] shaders = [new(program.Info, shader.Code, shader.Cb1Data)];
|
||||
|
||||
_compilationQueue.Enqueue(new ProgramCompilation([program], shaders, newSpecState, programIndex, isCompute: true));
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
private readonly GpuAccessorState _state;
|
||||
private readonly int _stageIndex;
|
||||
private readonly bool _compute;
|
||||
private readonly bool _isOpenGL;
|
||||
private readonly bool _isVulkan;
|
||||
private readonly bool _hasGeometryShader;
|
||||
private readonly bool _supportsQuads;
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
_channel = channel;
|
||||
_state = state;
|
||||
_stageIndex = stageIndex;
|
||||
_isOpenGL = context.Capabilities.Api == TargetApi.OpenGL;
|
||||
_isVulkan = context.Capabilities.Api == TargetApi.Vulkan;
|
||||
_hasGeometryShader = hasGeometryShader;
|
||||
_supportsQuads = context.Capabilities.SupportsQuads;
|
||||
|
||||
@@ -117,10 +117,10 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
public GpuGraphicsState QueryGraphicsState()
|
||||
{
|
||||
return _state.GraphicsState.CreateShaderGraphicsState(
|
||||
_isOpenGL,
|
||||
!_isVulkan,
|
||||
_supportsQuads,
|
||||
_hasGeometryShader,
|
||||
!_isOpenGL || _state.GraphicsState.YNegateEnabled);
|
||||
_isVulkan || _state.GraphicsState.YNegateEnabled);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
||||
@@ -55,7 +55,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
{
|
||||
int binding;
|
||||
|
||||
if (_context.Capabilities.Api != TargetApi.OpenGL)
|
||||
if (_context.Capabilities.Api == TargetApi.Vulkan)
|
||||
{
|
||||
binding = GetBindingFromIndex(index, _context.Capabilities.MaximumUniformBuffersPerStage, "Uniform buffer");
|
||||
}
|
||||
@@ -71,7 +71,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
{
|
||||
int binding;
|
||||
|
||||
if (_context.Capabilities.Api != TargetApi.OpenGL)
|
||||
if (_context.Capabilities.Api == TargetApi.Vulkan)
|
||||
{
|
||||
if (count == 1)
|
||||
{
|
||||
@@ -103,7 +103,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
{
|
||||
int binding;
|
||||
|
||||
if (_context.Capabilities.Api != TargetApi.OpenGL)
|
||||
if (_context.Capabilities.Api == TargetApi.Vulkan)
|
||||
{
|
||||
binding = GetBindingFromIndex(index, _context.Capabilities.MaximumStorageBuffersPerStage, "Storage buffer");
|
||||
}
|
||||
@@ -119,7 +119,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
{
|
||||
int binding;
|
||||
|
||||
if (_context.Capabilities.Api != TargetApi.OpenGL)
|
||||
if (_context.Capabilities.Api == TargetApi.Vulkan)
|
||||
{
|
||||
if (count == 1)
|
||||
{
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
using Ryujinx.Graphics.GAL;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Shader
|
||||
{
|
||||
/// <summary>
|
||||
@@ -63,14 +61,5 @@ 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.ToLower(), "cache", "shader")
|
||||
? Path.Combine(AppDataManager.GamesDirPath, GraphicsConfig.TitleId, "cache", "shader")
|
||||
: null;
|
||||
}
|
||||
|
||||
@@ -204,7 +204,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
GpuChannelComputeState computeState,
|
||||
ulong gpuVa)
|
||||
{
|
||||
if (_cpPrograms.TryGetValue(gpuVa, out CachedShaderProgram cpShader) && IsShaderEqual(channel, poolState, computeState, cpShader, gpuVa))
|
||||
if (_cpPrograms.TryGetValue(gpuVa, out var cpShader) && IsShaderEqual(channel, poolState, computeState, cpShader, gpuVa))
|
||||
{
|
||||
return cpShader;
|
||||
}
|
||||
@@ -223,11 +223,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
TranslatorContext translatorContext = DecodeComputeShader(gpuAccessor, _context.Capabilities.Api, gpuVa);
|
||||
TranslatedShader translatedShader = TranslateShader(_dumper, channel, translatorContext, cachedGuestCode, asCompute: false);
|
||||
|
||||
ShaderSource[] shaderSourcesArray = [CreateShaderSource(translatedShader.Program)];
|
||||
ShaderInfo info = ShaderInfoBuilder.BuildForCompute(
|
||||
_context,
|
||||
translatedShader.Program.Info,
|
||||
computeState.GetLocalSize());
|
||||
ShaderSource[] shaderSourcesArray = new ShaderSource[] { CreateShaderSource(translatedShader.Program) };
|
||||
ShaderInfo info = ShaderInfoBuilder.BuildForCompute(_context, translatedShader.Program.Info);
|
||||
IProgram hostProgram = _context.Renderer.CreateProgram(shaderSourcesArray, info);
|
||||
|
||||
cpShader = new CachedShaderProgram(hostProgram, specState, translatedShader.Shader);
|
||||
@@ -254,8 +251,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
{
|
||||
channel.TextureManager.UpdateRenderTargets();
|
||||
|
||||
RtControl rtControl = state.RtControl;
|
||||
TextureMsaaMode msaaMode = state.RtMsaaMode;
|
||||
var rtControl = state.RtControl;
|
||||
var msaaMode = state.RtMsaaMode;
|
||||
|
||||
pipeline.SamplesCount = msaaMode.SamplesInX() * msaaMode.SamplesInY();
|
||||
|
||||
@@ -265,7 +262,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
{
|
||||
int rtIndex = rtControl.UnpackPermutationIndex(index);
|
||||
|
||||
RtColorState colorState = state.RtColorState[rtIndex];
|
||||
var colorState = state.RtColorState[rtIndex];
|
||||
|
||||
if (index >= count || colorState.Format == 0 || colorState.WidthOrStride == 0)
|
||||
{
|
||||
@@ -310,12 +307,12 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
ref GpuChannelGraphicsState graphicsState,
|
||||
ShaderAddresses addresses)
|
||||
{
|
||||
if (_gpPrograms.TryGetValue(addresses, out CachedShaderProgram gpShaders) && IsShaderEqual(channel, ref poolState, ref graphicsState, gpShaders, addresses))
|
||||
if (_gpPrograms.TryGetValue(addresses, out var gpShaders) && IsShaderEqual(channel, ref poolState, ref graphicsState, gpShaders, addresses))
|
||||
{
|
||||
return gpShaders;
|
||||
}
|
||||
|
||||
if (_graphicsShaderCache.TryFind(channel, ref poolState, ref graphicsState, addresses, out gpShaders, out CachedGraphicsGuestCode cachedGuestCode))
|
||||
if (_graphicsShaderCache.TryFind(channel, ref poolState, ref graphicsState, addresses, out gpShaders, out var cachedGuestCode))
|
||||
{
|
||||
_gpPrograms[addresses] = gpShaders;
|
||||
return gpShaders;
|
||||
@@ -368,7 +365,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
bool geometryToCompute = ShouldConvertGeometryToCompute(_context, geometryHasStore);
|
||||
|
||||
CachedShaderStage[] shaders = new CachedShaderStage[Constants.ShaderStages + 1];
|
||||
List<ShaderSource> shaderSources = [];
|
||||
List<ShaderSource> shaderSources = new();
|
||||
|
||||
TranslatorContext previousStage = null;
|
||||
ShaderInfoBuilder infoBuilder = new(_context, transformFeedbackDescriptors != null, vertexToCompute);
|
||||
@@ -428,8 +425,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
|
||||
TranslatorContext lastInVertexPipeline = geometryToCompute ? translatorContexts[4] ?? currentStage : currentStage;
|
||||
|
||||
(program, ShaderProgramInfo vacInfo) = lastInVertexPipeline.GenerateVertexPassthroughForCompute();
|
||||
infoBuilder.AddStageInfoVac(vacInfo);
|
||||
program = lastInVertexPipeline.GenerateVertexPassthroughForCompute();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -534,9 +530,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, context.GetVertexAsComputeInfo(), tfEnabled);
|
||||
ShaderInfo info = ShaderInfoBuilder.BuildForVertexAsCompute(_context, program.Info, tfEnabled);
|
||||
|
||||
return new(_context.Renderer.CreateProgram([source], info), program.Info, context.GetResourceReservations());
|
||||
return new(_context.Renderer.CreateProgram(new[] { source }, info), program.Info, context.GetResourceReservations());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -586,7 +582,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
|
||||
for (int i = 0; i < Constants.TotalTransformFeedbackBuffers; i++)
|
||||
{
|
||||
TfState tf = state.TfState[i];
|
||||
var tf = state.TfState[i];
|
||||
|
||||
descs[i] = new TransformFeedbackDescriptor(
|
||||
tf.BufferIndex,
|
||||
@@ -692,7 +688,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
/// <returns>The generated translator context</returns>
|
||||
public static TranslatorContext DecodeComputeShader(IGpuAccessor gpuAccessor, TargetApi api, ulong gpuVa)
|
||||
{
|
||||
TranslationOptions options = CreateTranslationOptions(api, DefaultFlags | TranslationFlags.Compute);
|
||||
var options = CreateTranslationOptions(api, DefaultFlags | TranslationFlags.Compute);
|
||||
return Translator.CreateContext(gpuVa, gpuAccessor, options);
|
||||
}
|
||||
|
||||
@@ -709,7 +705,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
/// <returns>The generated translator context</returns>
|
||||
public static TranslatorContext DecodeGraphicsShader(IGpuAccessor gpuAccessor, TargetApi api, TranslationFlags flags, ulong gpuVa)
|
||||
{
|
||||
TranslationOptions options = CreateTranslationOptions(api, flags);
|
||||
var options = CreateTranslationOptions(api, flags);
|
||||
return Translator.CreateContext(gpuVa, gpuAccessor, options);
|
||||
}
|
||||
|
||||
@@ -735,7 +731,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
{
|
||||
ulong cb1DataAddress = channel.BufferManager.GetGraphicsUniformBufferAddress(0, 1);
|
||||
|
||||
MemoryManager memoryManager = channel.MemoryManager;
|
||||
var memoryManager = channel.MemoryManager;
|
||||
|
||||
codeA ??= memoryManager.GetSpan(vertexA.Address, vertexA.Size).ToArray();
|
||||
codeB ??= memoryManager.GetSpan(currentStage.Address, currentStage.Size).ToArray();
|
||||
@@ -773,7 +769,7 @@ 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)
|
||||
{
|
||||
MemoryManager memoryManager = channel.MemoryManager;
|
||||
var memoryManager = channel.MemoryManager;
|
||||
|
||||
ulong cb1DataAddress = context.Stage == ShaderStage.Compute
|
||||
? channel.BufferManager.GetComputeUniformBufferAddress(1)
|
||||
@@ -801,7 +797,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
{
|
||||
if (address == MemoryManager.PteUnmapped || size == 0)
|
||||
{
|
||||
return [];
|
||||
return Array.Empty<byte>();
|
||||
}
|
||||
|
||||
return memoryManager.Physical.GetSpan(address, size).ToArray();
|
||||
@@ -826,20 +822,16 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
|
||||
/// <summary>
|
||||
/// Creates shader translation options with the requested graphics API and flags.
|
||||
/// The shader language is chosen based on the current configuration and graphics API.
|
||||
/// The shader language is choosen 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 = api switch
|
||||
{
|
||||
TargetApi.OpenGL => TargetLanguage.Glsl,
|
||||
TargetApi.Vulkan => GraphicsConfig.EnableSpirvCompilationOnVulkan ? TargetLanguage.Spirv : TargetLanguage.Glsl,
|
||||
TargetApi.Metal => TargetLanguage.Msl,
|
||||
_ => throw new NotImplementedException()
|
||||
};
|
||||
TargetLanguage lang = GraphicsConfig.EnableSpirvCompilationOnVulkan && api == TargetApi.Vulkan
|
||||
? TargetLanguage.Spirv
|
||||
: TargetLanguage.Glsl;
|
||||
|
||||
return new TranslationOptions(lang, api, flags);
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
ResourceStages.Geometry;
|
||||
|
||||
private readonly GpuContext _context;
|
||||
private readonly ComputeSize _computeLocalSize;
|
||||
|
||||
private int _fragmentOutputMap;
|
||||
|
||||
@@ -40,11 +39,9 @@ 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>
|
||||
/// <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)
|
||||
public ShaderInfoBuilder(GpuContext context, bool tfEnabled, bool vertexAsCompute = false)
|
||||
{
|
||||
_context = context;
|
||||
_computeLocalSize = computeLocalSize;
|
||||
|
||||
_fragmentOutputMap = -1;
|
||||
|
||||
@@ -98,7 +95,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>
|
||||
@@ -162,25 +159,6 @@ 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>
|
||||
@@ -383,7 +361,14 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
|
||||
ResourceLayout resourceLayout = new(descriptors.AsReadOnly(), usages.AsReadOnly());
|
||||
|
||||
return new ShaderInfo(_fragmentOutputMap, resourceLayout, _computeLocalSize, pipeline, fromCache);
|
||||
if (pipeline.HasValue)
|
||||
{
|
||||
return new ShaderInfo(_fragmentOutputMap, resourceLayout, pipeline.Value, fromCache);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new ShaderInfo(_fragmentOutputMap, resourceLayout, fromCache);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -393,16 +378,14 @@ 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,
|
||||
ComputeSize computeLocalSize)
|
||||
bool tfEnabled)
|
||||
{
|
||||
ShaderInfoBuilder builder = new(context, tfEnabled, computeLocalSize: computeLocalSize);
|
||||
ShaderInfoBuilder builder = new(context, tfEnabled);
|
||||
|
||||
foreach (CachedShaderStage program in programs)
|
||||
{
|
||||
@@ -420,12 +403,11 @@ 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, ComputeSize computeLocalSize, bool fromCache = false)
|
||||
public static ShaderInfo BuildForCompute(GpuContext context, ShaderProgramInfo info, bool fromCache = false)
|
||||
{
|
||||
ShaderInfoBuilder builder = new(context, tfEnabled: false, vertexAsCompute: false, computeLocalSize: computeLocalSize);
|
||||
ShaderInfoBuilder builder = new(context, tfEnabled: false, vertexAsCompute: false);
|
||||
|
||||
builder.AddStageInfo(info);
|
||||
|
||||
@@ -440,11 +422,10 @@ 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, ShaderProgramInfo info2, bool tfEnabled, bool fromCache = false)
|
||||
public static ShaderInfo BuildForVertexAsCompute(GpuContext context, ShaderProgramInfo info, bool tfEnabled, bool fromCache = false)
|
||||
{
|
||||
ShaderInfoBuilder builder = new(context, tfEnabled, vertexAsCompute: true, computeLocalSize: ComputeSize.VtgAsCompute);
|
||||
ShaderInfoBuilder builder = new(context, tfEnabled, vertexAsCompute: true);
|
||||
|
||||
builder.AddStageInfoVac(info2);
|
||||
builder.AddStageInfo(info, vertexAsCompute: true);
|
||||
|
||||
return builder.Build(null, fromCache);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
<Configurations>Debug;Release;DebugAOT;ReleaseAOT</Configurations>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,146 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,157 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,385 +0,0 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,237 +0,0 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,294 +0,0 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,170 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,289 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
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() { }
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
using Ryujinx.Graphics.Metal.State;
|
||||
using SharpMetal.Metal;
|
||||
using System.Runtime.Versioning;
|
||||
|
||||
namespace Ryujinx.Graphics.Metal
|
||||
{
|
||||
[SupportedOSPlatform("macos")]
|
||||
class DepthStencilCache : StateCache<MTLDepthStencilState, DepthStencilUid, DepthStencilUid>
|
||||
{
|
||||
private readonly MTLDevice _device;
|
||||
|
||||
public DepthStencilCache(MTLDevice device)
|
||||
{
|
||||
_device = device;
|
||||
}
|
||||
|
||||
protected override DepthStencilUid GetHash(DepthStencilUid descriptor)
|
||||
{
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
protected override MTLDepthStencilState CreateValue(DepthStencilUid descriptor)
|
||||
{
|
||||
// Create descriptors
|
||||
|
||||
ref StencilUid frontUid = ref descriptor.FrontFace;
|
||||
|
||||
using MTLStencilDescriptor frontFaceStencil = new()
|
||||
{
|
||||
StencilFailureOperation = frontUid.StencilFailureOperation,
|
||||
DepthFailureOperation = frontUid.DepthFailureOperation,
|
||||
DepthStencilPassOperation = frontUid.DepthStencilPassOperation,
|
||||
StencilCompareFunction = frontUid.StencilCompareFunction,
|
||||
ReadMask = frontUid.ReadMask,
|
||||
WriteMask = frontUid.WriteMask
|
||||
};
|
||||
|
||||
ref StencilUid backUid = ref descriptor.BackFace;
|
||||
|
||||
using MTLStencilDescriptor backFaceStencil = new()
|
||||
{
|
||||
StencilFailureOperation = backUid.StencilFailureOperation,
|
||||
DepthFailureOperation = backUid.DepthFailureOperation,
|
||||
DepthStencilPassOperation = backUid.DepthStencilPassOperation,
|
||||
StencilCompareFunction = backUid.StencilCompareFunction,
|
||||
ReadMask = backUid.ReadMask,
|
||||
WriteMask = backUid.WriteMask
|
||||
};
|
||||
|
||||
MTLDepthStencilDescriptor mtlDescriptor = new()
|
||||
{
|
||||
DepthCompareFunction = descriptor.DepthCompareFunction,
|
||||
DepthWriteEnabled = descriptor.DepthWriteEnabled
|
||||
};
|
||||
|
||||
if (descriptor.StencilTestEnabled)
|
||||
{
|
||||
mtlDescriptor.BackFaceStencil = backFaceStencil;
|
||||
mtlDescriptor.FrontFaceStencil = frontFaceStencil;
|
||||
}
|
||||
|
||||
using (mtlDescriptor)
|
||||
{
|
||||
return _device.NewDepthStencilState(mtlDescriptor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
using SharpMetal.Metal;
|
||||
using System;
|
||||
using System.Runtime.Versioning;
|
||||
|
||||
namespace Ryujinx.Graphics.Metal
|
||||
{
|
||||
[SupportedOSPlatform("macos")]
|
||||
readonly struct DisposableBuffer : IDisposable
|
||||
{
|
||||
public MTLBuffer Value { get; }
|
||||
|
||||
public DisposableBuffer(MTLBuffer buffer)
|
||||
{
|
||||
Value = buffer;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (Value != IntPtr.Zero)
|
||||
{
|
||||
Value.SetPurgeableState(MTLPurgeableState.Empty);
|
||||
Value.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
using SharpMetal.Metal;
|
||||
using System;
|
||||
using System.Runtime.Versioning;
|
||||
|
||||
namespace Ryujinx.Graphics.Metal
|
||||
{
|
||||
[SupportedOSPlatform("macos")]
|
||||
readonly struct DisposableSampler : IDisposable
|
||||
{
|
||||
public MTLSamplerState Value { get; }
|
||||
|
||||
public DisposableSampler(MTLSamplerState sampler)
|
||||
{
|
||||
Value = sampler;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Value.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Metal.Effects
|
||||
{
|
||||
internal interface IPostProcessingEffect : IDisposable
|
||||
{
|
||||
const int LocalGroupSize = 64;
|
||||
Texture Run(Texture view, int width, int height);
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Metal.Effects
|
||||
{
|
||||
internal interface IScalingFilter : IDisposable
|
||||
{
|
||||
float Level { get; set; }
|
||||
void Run(
|
||||
Texture view,
|
||||
Texture destinationTexture,
|
||||
Format format,
|
||||
int width,
|
||||
int height,
|
||||
Extents2D source,
|
||||
Extents2D destination);
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
using SharpMetal.Metal;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Graphics.Metal
|
||||
{
|
||||
public struct RenderEncoderBindings
|
||||
{
|
||||
public List<Resource> Resources = [];
|
||||
public List<BufferResource> VertexBuffers = [];
|
||||
public List<BufferResource> FragmentBuffers = [];
|
||||
|
||||
public RenderEncoderBindings() { }
|
||||
|
||||
public readonly void Clear()
|
||||
{
|
||||
Resources.Clear();
|
||||
VertexBuffers.Clear();
|
||||
FragmentBuffers.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public struct ComputeEncoderBindings
|
||||
{
|
||||
public List<Resource> Resources = [];
|
||||
public List<BufferResource> Buffers = [];
|
||||
|
||||
public ComputeEncoderBindings() { }
|
||||
|
||||
public readonly void Clear()
|
||||
{
|
||||
Resources.Clear();
|
||||
Buffers.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public struct BufferResource
|
||||
{
|
||||
public MTLBuffer Buffer;
|
||||
public ulong Offset;
|
||||
public ulong Binding;
|
||||
|
||||
public BufferResource(MTLBuffer buffer, ulong offset, ulong binding)
|
||||
{
|
||||
Buffer = buffer;
|
||||
Offset = offset;
|
||||
Binding = binding;
|
||||
}
|
||||
}
|
||||
|
||||
public struct Resource
|
||||
{
|
||||
public MTLResource MtlResource;
|
||||
public MTLResourceUsage ResourceUsage;
|
||||
public MTLRenderStages Stages;
|
||||
|
||||
public Resource(MTLResource resource, MTLResourceUsage resourceUsage, MTLRenderStages stages)
|
||||
{
|
||||
MtlResource = resource;
|
||||
ResourceUsage = resourceUsage;
|
||||
Stages = stages;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,206 +0,0 @@
|
||||
using Ryujinx.Common.Memory;
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.Metal.State;
|
||||
using Ryujinx.Graphics.Shader;
|
||||
using SharpMetal.Metal;
|
||||
using System;
|
||||
using System.Runtime.Versioning;
|
||||
|
||||
namespace Ryujinx.Graphics.Metal
|
||||
{
|
||||
[Flags]
|
||||
enum DirtyFlags
|
||||
{
|
||||
None = 0,
|
||||
RenderPipeline = 1 << 0,
|
||||
ComputePipeline = 1 << 1,
|
||||
DepthStencil = 1 << 2,
|
||||
DepthClamp = 1 << 3,
|
||||
DepthBias = 1 << 4,
|
||||
CullMode = 1 << 5,
|
||||
FrontFace = 1 << 6,
|
||||
StencilRef = 1 << 7,
|
||||
Viewports = 1 << 8,
|
||||
Scissors = 1 << 9,
|
||||
Uniforms = 1 << 10,
|
||||
Storages = 1 << 11,
|
||||
Textures = 1 << 12,
|
||||
Images = 1 << 13,
|
||||
|
||||
ArgBuffers = Uniforms | Storages | Textures | Images,
|
||||
|
||||
RenderAll = RenderPipeline | DepthStencil | DepthClamp | DepthBias | CullMode | FrontFace | StencilRef | Viewports | Scissors | ArgBuffers,
|
||||
ComputeAll = ComputePipeline | ArgBuffers,
|
||||
All = RenderAll | ComputeAll,
|
||||
}
|
||||
|
||||
record struct BufferRef
|
||||
{
|
||||
public Auto<DisposableBuffer> Buffer;
|
||||
public BufferRange? Range;
|
||||
|
||||
public BufferRef(Auto<DisposableBuffer> buffer)
|
||||
{
|
||||
Buffer = buffer;
|
||||
}
|
||||
|
||||
public BufferRef(Auto<DisposableBuffer> buffer, ref BufferRange range)
|
||||
{
|
||||
Buffer = buffer;
|
||||
Range = range;
|
||||
}
|
||||
}
|
||||
|
||||
record struct TextureRef
|
||||
{
|
||||
public ShaderStage Stage;
|
||||
public TextureBase Storage;
|
||||
public Auto<DisposableSampler> Sampler;
|
||||
public Format ImageFormat;
|
||||
|
||||
public TextureRef(ShaderStage stage, TextureBase storage, Auto<DisposableSampler> sampler)
|
||||
{
|
||||
Stage = stage;
|
||||
Storage = storage;
|
||||
Sampler = sampler;
|
||||
}
|
||||
}
|
||||
|
||||
record struct ImageRef
|
||||
{
|
||||
public ShaderStage Stage;
|
||||
public Texture Storage;
|
||||
|
||||
public ImageRef(ShaderStage stage, Texture storage)
|
||||
{
|
||||
Stage = stage;
|
||||
Storage = storage;
|
||||
}
|
||||
}
|
||||
|
||||
struct PredrawState
|
||||
{
|
||||
public MTLCullMode CullMode;
|
||||
public DepthStencilUid DepthStencilUid;
|
||||
public PrimitiveTopology Topology;
|
||||
public MTLViewport[] Viewports;
|
||||
}
|
||||
|
||||
struct RenderTargetCopy
|
||||
{
|
||||
public MTLScissorRect[] Scissors;
|
||||
public Texture DepthStencil;
|
||||
public Texture[] RenderTargets;
|
||||
}
|
||||
|
||||
[SupportedOSPlatform("macos")]
|
||||
class EncoderState
|
||||
{
|
||||
public Program RenderProgram = null;
|
||||
public Program ComputeProgram = null;
|
||||
|
||||
public PipelineState Pipeline;
|
||||
public DepthStencilUid DepthStencilUid;
|
||||
|
||||
public readonly record struct ArrayRef<T>(ShaderStage Stage, T Array);
|
||||
|
||||
public readonly BufferRef[] UniformBufferRefs = new BufferRef[Constants.MaxUniformBufferBindings];
|
||||
public readonly BufferRef[] StorageBufferRefs = new BufferRef[Constants.MaxStorageBufferBindings];
|
||||
public readonly TextureRef[] TextureRefs = new TextureRef[Constants.MaxTextureBindings * 2];
|
||||
public readonly ImageRef[] ImageRefs = new ImageRef[Constants.MaxImageBindings * 2];
|
||||
|
||||
public ArrayRef<TextureArray>[] TextureArrayRefs = [];
|
||||
public ArrayRef<ImageArray>[] ImageArrayRefs = [];
|
||||
|
||||
public ArrayRef<TextureArray>[] TextureArrayExtraRefs = [];
|
||||
public ArrayRef<ImageArray>[] ImageArrayExtraRefs = [];
|
||||
|
||||
public IndexBufferState IndexBuffer = default;
|
||||
|
||||
public MTLDepthClipMode DepthClipMode = MTLDepthClipMode.Clip;
|
||||
|
||||
public float DepthBias;
|
||||
public float SlopeScale;
|
||||
public float Clamp;
|
||||
|
||||
public int BackRefValue = 0;
|
||||
public int FrontRefValue = 0;
|
||||
|
||||
public PrimitiveTopology Topology = PrimitiveTopology.Triangles;
|
||||
public MTLCullMode CullMode = MTLCullMode.None;
|
||||
public MTLWinding Winding = MTLWinding.CounterClockwise;
|
||||
public bool CullBoth = false;
|
||||
|
||||
public MTLViewport[] Viewports = new MTLViewport[Constants.MaxViewports];
|
||||
public MTLScissorRect[] Scissors = new MTLScissorRect[Constants.MaxViewports];
|
||||
|
||||
// Changes to attachments take recreation!
|
||||
public Texture DepthStencil;
|
||||
public Texture[] RenderTargets = new Texture[Constants.MaxColorAttachments];
|
||||
public ITexture PreMaskDepthStencil = default;
|
||||
public ITexture[] PreMaskRenderTargets;
|
||||
public bool FramebufferUsingColorWriteMask;
|
||||
|
||||
public Array8<ColorBlendStateUid> StoredBlend;
|
||||
public ColorF BlendColor = new();
|
||||
|
||||
public readonly VertexBufferState[] VertexBuffers = new VertexBufferState[Constants.MaxVertexBuffers];
|
||||
public readonly VertexAttribDescriptor[] VertexAttribs = new VertexAttribDescriptor[Constants.MaxVertexAttributes];
|
||||
// Dirty flags
|
||||
public DirtyFlags Dirty = DirtyFlags.None;
|
||||
|
||||
// Only to be used for present
|
||||
public bool ClearLoadAction = false;
|
||||
|
||||
public RenderEncoderBindings RenderEncoderBindings = new();
|
||||
public ComputeEncoderBindings ComputeEncoderBindings = new();
|
||||
|
||||
public EncoderState()
|
||||
{
|
||||
Pipeline.Initialize();
|
||||
DepthStencilUid.DepthCompareFunction = MTLCompareFunction.Always;
|
||||
}
|
||||
|
||||
public RenderTargetCopy InheritForClear(EncoderState other, bool depth, int singleIndex = -1)
|
||||
{
|
||||
// Inherit render target related information without causing a render encoder split.
|
||||
|
||||
RenderTargetCopy oldState = new()
|
||||
{
|
||||
Scissors = other.Scissors,
|
||||
RenderTargets = other.RenderTargets,
|
||||
DepthStencil = other.DepthStencil
|
||||
};
|
||||
|
||||
Scissors = other.Scissors;
|
||||
RenderTargets = other.RenderTargets;
|
||||
DepthStencil = other.DepthStencil;
|
||||
|
||||
Pipeline.ColorBlendAttachmentStateCount = other.Pipeline.ColorBlendAttachmentStateCount;
|
||||
Pipeline.Internal.ColorBlendState = other.Pipeline.Internal.ColorBlendState;
|
||||
Pipeline.DepthStencilFormat = other.Pipeline.DepthStencilFormat;
|
||||
|
||||
ref Array8<ColorBlendStateUid> blendStates = ref Pipeline.Internal.ColorBlendState;
|
||||
|
||||
// Mask out irrelevant attachments.
|
||||
for (int i = 0; i < blendStates.Length; i++)
|
||||
{
|
||||
if (depth || (singleIndex != -1 && singleIndex != i))
|
||||
{
|
||||
blendStates[i].WriteMask = MTLColorWriteMask.None;
|
||||
}
|
||||
}
|
||||
|
||||
return oldState;
|
||||
}
|
||||
|
||||
public void Restore(RenderTargetCopy copy)
|
||||
{
|
||||
Scissors = copy.Scissors;
|
||||
RenderTargets = copy.RenderTargets;
|
||||
DepthStencil = copy.DepthStencil;
|
||||
|
||||
Pipeline.Internal.ResetColorState();
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,292 +0,0 @@
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using SharpMetal.Metal;
|
||||
using System.Runtime.Versioning;
|
||||
|
||||
namespace Ryujinx.Graphics.Metal
|
||||
{
|
||||
[SupportedOSPlatform("macos")]
|
||||
static class EnumConversion
|
||||
{
|
||||
public static MTLSamplerAddressMode Convert(this AddressMode mode)
|
||||
{
|
||||
return mode switch
|
||||
{
|
||||
AddressMode.Clamp => MTLSamplerAddressMode.ClampToEdge, // TODO: Should be clamp.
|
||||
AddressMode.Repeat => MTLSamplerAddressMode.Repeat,
|
||||
AddressMode.MirrorClamp => MTLSamplerAddressMode.MirrorClampToEdge, // TODO: Should be mirror clamp.
|
||||
AddressMode.MirroredRepeat => MTLSamplerAddressMode.MirrorRepeat,
|
||||
AddressMode.ClampToBorder => MTLSamplerAddressMode.ClampToBorderColor,
|
||||
AddressMode.ClampToEdge => MTLSamplerAddressMode.ClampToEdge,
|
||||
AddressMode.MirrorClampToEdge => MTLSamplerAddressMode.MirrorClampToEdge,
|
||||
AddressMode.MirrorClampToBorder => MTLSamplerAddressMode.ClampToBorderColor, // TODO: Should be mirror clamp to border.
|
||||
_ => LogInvalidAndReturn(mode, nameof(AddressMode), MTLSamplerAddressMode.ClampToEdge) // TODO: Should be clamp.
|
||||
};
|
||||
}
|
||||
|
||||
public static MTLBlendFactor Convert(this BlendFactor factor)
|
||||
{
|
||||
return factor switch
|
||||
{
|
||||
BlendFactor.Zero or BlendFactor.ZeroGl => MTLBlendFactor.Zero,
|
||||
BlendFactor.One or BlendFactor.OneGl => MTLBlendFactor.One,
|
||||
BlendFactor.SrcColor or BlendFactor.SrcColorGl => MTLBlendFactor.SourceColor,
|
||||
BlendFactor.OneMinusSrcColor or BlendFactor.OneMinusSrcColorGl => MTLBlendFactor.OneMinusSourceColor,
|
||||
BlendFactor.SrcAlpha or BlendFactor.SrcAlphaGl => MTLBlendFactor.SourceAlpha,
|
||||
BlendFactor.OneMinusSrcAlpha or BlendFactor.OneMinusSrcAlphaGl => MTLBlendFactor.OneMinusSourceAlpha,
|
||||
BlendFactor.DstAlpha or BlendFactor.DstAlphaGl => MTLBlendFactor.DestinationAlpha,
|
||||
BlendFactor.OneMinusDstAlpha or BlendFactor.OneMinusDstAlphaGl => MTLBlendFactor.OneMinusDestinationAlpha,
|
||||
BlendFactor.DstColor or BlendFactor.DstColorGl => MTLBlendFactor.DestinationColor,
|
||||
BlendFactor.OneMinusDstColor or BlendFactor.OneMinusDstColorGl => MTLBlendFactor.OneMinusDestinationColor,
|
||||
BlendFactor.SrcAlphaSaturate or BlendFactor.SrcAlphaSaturateGl => MTLBlendFactor.SourceAlphaSaturated,
|
||||
BlendFactor.Src1Color or BlendFactor.Src1ColorGl => MTLBlendFactor.Source1Color,
|
||||
BlendFactor.OneMinusSrc1Color or BlendFactor.OneMinusSrc1ColorGl => MTLBlendFactor.OneMinusSource1Color,
|
||||
BlendFactor.Src1Alpha or BlendFactor.Src1AlphaGl => MTLBlendFactor.Source1Alpha,
|
||||
BlendFactor.OneMinusSrc1Alpha or BlendFactor.OneMinusSrc1AlphaGl => MTLBlendFactor.OneMinusSource1Alpha,
|
||||
BlendFactor.ConstantColor => MTLBlendFactor.BlendColor,
|
||||
BlendFactor.OneMinusConstantColor => MTLBlendFactor.OneMinusBlendColor,
|
||||
BlendFactor.ConstantAlpha => MTLBlendFactor.BlendAlpha,
|
||||
BlendFactor.OneMinusConstantAlpha => MTLBlendFactor.OneMinusBlendAlpha,
|
||||
_ => LogInvalidAndReturn(factor, nameof(BlendFactor), MTLBlendFactor.Zero)
|
||||
};
|
||||
}
|
||||
|
||||
public static MTLBlendOperation Convert(this BlendOp op)
|
||||
{
|
||||
return op switch
|
||||
{
|
||||
BlendOp.Add or BlendOp.AddGl => MTLBlendOperation.Add,
|
||||
BlendOp.Subtract or BlendOp.SubtractGl => MTLBlendOperation.Subtract,
|
||||
BlendOp.ReverseSubtract or BlendOp.ReverseSubtractGl => MTLBlendOperation.ReverseSubtract,
|
||||
BlendOp.Minimum => MTLBlendOperation.Min,
|
||||
BlendOp.Maximum => MTLBlendOperation.Max,
|
||||
_ => LogInvalidAndReturn(op, nameof(BlendOp), MTLBlendOperation.Add)
|
||||
};
|
||||
}
|
||||
|
||||
public static MTLCompareFunction Convert(this CompareOp op)
|
||||
{
|
||||
return op switch
|
||||
{
|
||||
CompareOp.Never or CompareOp.NeverGl => MTLCompareFunction.Never,
|
||||
CompareOp.Less or CompareOp.LessGl => MTLCompareFunction.Less,
|
||||
CompareOp.Equal or CompareOp.EqualGl => MTLCompareFunction.Equal,
|
||||
CompareOp.LessOrEqual or CompareOp.LessOrEqualGl => MTLCompareFunction.LessEqual,
|
||||
CompareOp.Greater or CompareOp.GreaterGl => MTLCompareFunction.Greater,
|
||||
CompareOp.NotEqual or CompareOp.NotEqualGl => MTLCompareFunction.NotEqual,
|
||||
CompareOp.GreaterOrEqual or CompareOp.GreaterOrEqualGl => MTLCompareFunction.GreaterEqual,
|
||||
CompareOp.Always or CompareOp.AlwaysGl => MTLCompareFunction.Always,
|
||||
_ => LogInvalidAndReturn(op, nameof(CompareOp), MTLCompareFunction.Never)
|
||||
};
|
||||
}
|
||||
|
||||
public static MTLCullMode Convert(this Face face)
|
||||
{
|
||||
return face switch
|
||||
{
|
||||
Face.Back => MTLCullMode.Back,
|
||||
Face.Front => MTLCullMode.Front,
|
||||
Face.FrontAndBack => MTLCullMode.None,
|
||||
_ => LogInvalidAndReturn(face, nameof(Face), MTLCullMode.Back)
|
||||
};
|
||||
}
|
||||
|
||||
public static MTLWinding Convert(this FrontFace frontFace)
|
||||
{
|
||||
// The viewport is flipped vertically, therefore we need to switch the winding order as well
|
||||
return frontFace switch
|
||||
{
|
||||
FrontFace.Clockwise => MTLWinding.CounterClockwise,
|
||||
FrontFace.CounterClockwise => MTLWinding.Clockwise,
|
||||
_ => LogInvalidAndReturn(frontFace, nameof(FrontFace), MTLWinding.Clockwise)
|
||||
};
|
||||
}
|
||||
|
||||
public static MTLIndexType Convert(this IndexType type)
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
IndexType.UShort => MTLIndexType.UInt16,
|
||||
IndexType.UInt => MTLIndexType.UInt32,
|
||||
_ => LogInvalidAndReturn(type, nameof(IndexType), MTLIndexType.UInt16)
|
||||
};
|
||||
}
|
||||
|
||||
public static MTLLogicOperation Convert(this LogicalOp op)
|
||||
{
|
||||
return op switch
|
||||
{
|
||||
LogicalOp.Clear => MTLLogicOperation.Clear,
|
||||
LogicalOp.And => MTLLogicOperation.And,
|
||||
LogicalOp.AndReverse => MTLLogicOperation.AndReverse,
|
||||
LogicalOp.Copy => MTLLogicOperation.Copy,
|
||||
LogicalOp.AndInverted => MTLLogicOperation.AndInverted,
|
||||
LogicalOp.Noop => MTLLogicOperation.Noop,
|
||||
LogicalOp.Xor => MTLLogicOperation.Xor,
|
||||
LogicalOp.Or => MTLLogicOperation.Or,
|
||||
LogicalOp.Nor => MTLLogicOperation.Nor,
|
||||
LogicalOp.Equiv => MTLLogicOperation.Equivalence,
|
||||
LogicalOp.Invert => MTLLogicOperation.Invert,
|
||||
LogicalOp.OrReverse => MTLLogicOperation.OrReverse,
|
||||
LogicalOp.CopyInverted => MTLLogicOperation.CopyInverted,
|
||||
LogicalOp.OrInverted => MTLLogicOperation.OrInverted,
|
||||
LogicalOp.Nand => MTLLogicOperation.Nand,
|
||||
LogicalOp.Set => MTLLogicOperation.Set,
|
||||
_ => LogInvalidAndReturn(op, nameof(LogicalOp), MTLLogicOperation.And)
|
||||
};
|
||||
}
|
||||
|
||||
public static MTLSamplerMinMagFilter Convert(this MagFilter filter)
|
||||
{
|
||||
return filter switch
|
||||
{
|
||||
MagFilter.Nearest => MTLSamplerMinMagFilter.Nearest,
|
||||
MagFilter.Linear => MTLSamplerMinMagFilter.Linear,
|
||||
_ => LogInvalidAndReturn(filter, nameof(MagFilter), MTLSamplerMinMagFilter.Nearest)
|
||||
};
|
||||
}
|
||||
|
||||
public static (MTLSamplerMinMagFilter, MTLSamplerMipFilter) Convert(this MinFilter filter)
|
||||
{
|
||||
return filter switch
|
||||
{
|
||||
MinFilter.Nearest => (MTLSamplerMinMagFilter.Nearest, MTLSamplerMipFilter.Nearest),
|
||||
MinFilter.Linear => (MTLSamplerMinMagFilter.Linear, MTLSamplerMipFilter.Linear),
|
||||
MinFilter.NearestMipmapNearest => (MTLSamplerMinMagFilter.Nearest, MTLSamplerMipFilter.Nearest),
|
||||
MinFilter.LinearMipmapNearest => (MTLSamplerMinMagFilter.Linear, MTLSamplerMipFilter.Nearest),
|
||||
MinFilter.NearestMipmapLinear => (MTLSamplerMinMagFilter.Nearest, MTLSamplerMipFilter.Linear),
|
||||
MinFilter.LinearMipmapLinear => (MTLSamplerMinMagFilter.Linear, MTLSamplerMipFilter.Linear),
|
||||
_ => LogInvalidAndReturn(filter, nameof(MinFilter), (MTLSamplerMinMagFilter.Nearest, MTLSamplerMipFilter.Nearest))
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
public static MTLPrimitiveType Convert(this PrimitiveTopology topology)
|
||||
{
|
||||
return topology switch
|
||||
{
|
||||
PrimitiveTopology.Points => MTLPrimitiveType.Point,
|
||||
PrimitiveTopology.Lines => MTLPrimitiveType.Line,
|
||||
PrimitiveTopology.LineStrip => MTLPrimitiveType.LineStrip,
|
||||
PrimitiveTopology.Triangles => MTLPrimitiveType.Triangle,
|
||||
PrimitiveTopology.TriangleStrip => MTLPrimitiveType.TriangleStrip,
|
||||
_ => LogInvalidAndReturn(topology, nameof(PrimitiveTopology), MTLPrimitiveType.Triangle)
|
||||
};
|
||||
}
|
||||
|
||||
public static MTLStencilOperation Convert(this StencilOp op)
|
||||
{
|
||||
return op switch
|
||||
{
|
||||
StencilOp.Keep or StencilOp.KeepGl => MTLStencilOperation.Keep,
|
||||
StencilOp.Zero or StencilOp.ZeroGl => MTLStencilOperation.Zero,
|
||||
StencilOp.Replace or StencilOp.ReplaceGl => MTLStencilOperation.Replace,
|
||||
StencilOp.IncrementAndClamp or StencilOp.IncrementAndClampGl => MTLStencilOperation.IncrementClamp,
|
||||
StencilOp.DecrementAndClamp or StencilOp.DecrementAndClampGl => MTLStencilOperation.DecrementClamp,
|
||||
StencilOp.Invert or StencilOp.InvertGl => MTLStencilOperation.Invert,
|
||||
StencilOp.IncrementAndWrap or StencilOp.IncrementAndWrapGl => MTLStencilOperation.IncrementWrap,
|
||||
StencilOp.DecrementAndWrap or StencilOp.DecrementAndWrapGl => MTLStencilOperation.DecrementWrap,
|
||||
_ => LogInvalidAndReturn(op, nameof(StencilOp), MTLStencilOperation.Keep)
|
||||
};
|
||||
}
|
||||
|
||||
public static MTLTextureType Convert(this Target target)
|
||||
{
|
||||
return target switch
|
||||
{
|
||||
Target.TextureBuffer => MTLTextureType.TextureBuffer,
|
||||
Target.Texture1D => MTLTextureType.Type1D,
|
||||
Target.Texture1DArray => MTLTextureType.Type1DArray,
|
||||
Target.Texture2D => MTLTextureType.Type2D,
|
||||
Target.Texture2DArray => MTLTextureType.Type2DArray,
|
||||
Target.Texture2DMultisample => MTLTextureType.Type2DMultisample,
|
||||
Target.Texture2DMultisampleArray => MTLTextureType.Type2DMultisampleArray,
|
||||
Target.Texture3D => MTLTextureType.Type3D,
|
||||
Target.Cubemap => MTLTextureType.Cube,
|
||||
Target.CubemapArray => MTLTextureType.CubeArray,
|
||||
_ => LogInvalidAndReturn(target, nameof(Target), MTLTextureType.Type2D)
|
||||
};
|
||||
}
|
||||
|
||||
public static MTLTextureSwizzle Convert(this SwizzleComponent swizzleComponent)
|
||||
{
|
||||
return swizzleComponent switch
|
||||
{
|
||||
SwizzleComponent.Zero => MTLTextureSwizzle.Zero,
|
||||
SwizzleComponent.One => MTLTextureSwizzle.One,
|
||||
SwizzleComponent.Red => MTLTextureSwizzle.Red,
|
||||
SwizzleComponent.Green => MTLTextureSwizzle.Green,
|
||||
SwizzleComponent.Blue => MTLTextureSwizzle.Blue,
|
||||
SwizzleComponent.Alpha => MTLTextureSwizzle.Alpha,
|
||||
_ => LogInvalidAndReturn(swizzleComponent, nameof(SwizzleComponent), MTLTextureSwizzle.Zero)
|
||||
};
|
||||
}
|
||||
|
||||
public static MTLVertexFormat Convert(this Format format)
|
||||
{
|
||||
return format switch
|
||||
{
|
||||
Format.R16Float => MTLVertexFormat.Half,
|
||||
Format.R16G16Float => MTLVertexFormat.Half2,
|
||||
Format.R16G16B16Float => MTLVertexFormat.Half3,
|
||||
Format.R16G16B16A16Float => MTLVertexFormat.Half4,
|
||||
Format.R32Float => MTLVertexFormat.Float,
|
||||
Format.R32G32Float => MTLVertexFormat.Float2,
|
||||
Format.R32G32B32Float => MTLVertexFormat.Float3,
|
||||
Format.R11G11B10Float => MTLVertexFormat.FloatRG11B10,
|
||||
Format.R32G32B32A32Float => MTLVertexFormat.Float4,
|
||||
Format.R8Uint => MTLVertexFormat.UChar,
|
||||
Format.R8G8Uint => MTLVertexFormat.UChar2,
|
||||
Format.R8G8B8Uint => MTLVertexFormat.UChar3,
|
||||
Format.R8G8B8A8Uint => MTLVertexFormat.UChar4,
|
||||
Format.R16Uint => MTLVertexFormat.UShort,
|
||||
Format.R16G16Uint => MTLVertexFormat.UShort2,
|
||||
Format.R16G16B16Uint => MTLVertexFormat.UShort3,
|
||||
Format.R16G16B16A16Uint => MTLVertexFormat.UShort4,
|
||||
Format.R32Uint => MTLVertexFormat.UInt,
|
||||
Format.R32G32Uint => MTLVertexFormat.UInt2,
|
||||
Format.R32G32B32Uint => MTLVertexFormat.UInt3,
|
||||
Format.R32G32B32A32Uint => MTLVertexFormat.UInt4,
|
||||
Format.R8Sint => MTLVertexFormat.Char,
|
||||
Format.R8G8Sint => MTLVertexFormat.Char2,
|
||||
Format.R8G8B8Sint => MTLVertexFormat.Char3,
|
||||
Format.R8G8B8A8Sint => MTLVertexFormat.Char4,
|
||||
Format.R16Sint => MTLVertexFormat.Short,
|
||||
Format.R16G16Sint => MTLVertexFormat.Short2,
|
||||
Format.R16G16B16Sint => MTLVertexFormat.Short3,
|
||||
Format.R16G16B16A16Sint => MTLVertexFormat.Short4,
|
||||
Format.R32Sint => MTLVertexFormat.Int,
|
||||
Format.R32G32Sint => MTLVertexFormat.Int2,
|
||||
Format.R32G32B32Sint => MTLVertexFormat.Int3,
|
||||
Format.R32G32B32A32Sint => MTLVertexFormat.Int4,
|
||||
Format.R8Unorm => MTLVertexFormat.UCharNormalized,
|
||||
Format.R8G8Unorm => MTLVertexFormat.UChar2Normalized,
|
||||
Format.R8G8B8Unorm => MTLVertexFormat.UChar3Normalized,
|
||||
Format.R8G8B8A8Unorm => MTLVertexFormat.UChar4Normalized,
|
||||
Format.R16Unorm => MTLVertexFormat.UShortNormalized,
|
||||
Format.R16G16Unorm => MTLVertexFormat.UShort2Normalized,
|
||||
Format.R16G16B16Unorm => MTLVertexFormat.UShort3Normalized,
|
||||
Format.R16G16B16A16Unorm => MTLVertexFormat.UShort4Normalized,
|
||||
Format.R10G10B10A2Unorm => MTLVertexFormat.UInt1010102Normalized,
|
||||
Format.R8Snorm => MTLVertexFormat.CharNormalized,
|
||||
Format.R8G8Snorm => MTLVertexFormat.Char2Normalized,
|
||||
Format.R8G8B8Snorm => MTLVertexFormat.Char3Normalized,
|
||||
Format.R8G8B8A8Snorm => MTLVertexFormat.Char4Normalized,
|
||||
Format.R16Snorm => MTLVertexFormat.ShortNormalized,
|
||||
Format.R16G16Snorm => MTLVertexFormat.Short2Normalized,
|
||||
Format.R16G16B16Snorm => MTLVertexFormat.Short3Normalized,
|
||||
Format.R16G16B16A16Snorm => MTLVertexFormat.Short4Normalized,
|
||||
Format.R10G10B10A2Snorm => MTLVertexFormat.Int1010102Normalized,
|
||||
|
||||
_ => LogInvalidAndReturn(format, nameof(Format), MTLVertexFormat.Float4)
|
||||
};
|
||||
}
|
||||
|
||||
private static T2 LogInvalidAndReturn<T1, T2>(T1 value, string name, T2 defaultValue = default)
|
||||
{
|
||||
Logger.Debug?.Print(LogClass.Gpu, $"Invalid {name} enum value: {value}.");
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
using SharpMetal.Metal;
|
||||
using System;
|
||||
using System.Runtime.Versioning;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.Graphics.Metal
|
||||
{
|
||||
[SupportedOSPlatform("macos")]
|
||||
class FenceHolder : IDisposable
|
||||
{
|
||||
private MTLCommandBuffer _fence;
|
||||
private int _referenceCount;
|
||||
private bool _disposed;
|
||||
|
||||
public FenceHolder(MTLCommandBuffer fence)
|
||||
{
|
||||
_fence = fence;
|
||||
_referenceCount = 1;
|
||||
}
|
||||
|
||||
public MTLCommandBuffer GetUnsafe()
|
||||
{
|
||||
return _fence;
|
||||
}
|
||||
|
||||
public bool TryGet(out MTLCommandBuffer fence)
|
||||
{
|
||||
int lastValue;
|
||||
do
|
||||
{
|
||||
lastValue = _referenceCount;
|
||||
|
||||
if (lastValue == 0)
|
||||
{
|
||||
fence = default;
|
||||
return false;
|
||||
}
|
||||
} while (Interlocked.CompareExchange(ref _referenceCount, lastValue + 1, lastValue) != lastValue);
|
||||
|
||||
fence = _fence;
|
||||
return true;
|
||||
}
|
||||
|
||||
public MTLCommandBuffer Get()
|
||||
{
|
||||
Interlocked.Increment(ref _referenceCount);
|
||||
return _fence;
|
||||
}
|
||||
|
||||
public void Put()
|
||||
{
|
||||
if (Interlocked.Decrement(ref _referenceCount) == 0)
|
||||
{
|
||||
_fence = default;
|
||||
}
|
||||
}
|
||||
|
||||
public void Wait()
|
||||
{
|
||||
_fence.WaitUntilCompleted();
|
||||
}
|
||||
|
||||
public bool IsSignaled()
|
||||
{
|
||||
return _fence.Status == MTLCommandBufferStatus.Completed;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
Put();
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Graphics.Metal
|
||||
{
|
||||
class FormatConverter
|
||||
{
|
||||
public static void ConvertD24S8ToD32FS8(Span<byte> output, ReadOnlySpan<byte> input)
|
||||
{
|
||||
const float UnormToFloat = 1f / 0xffffff;
|
||||
|
||||
Span<uint> outputUint = MemoryMarshal.Cast<byte, uint>(output);
|
||||
ReadOnlySpan<uint> inputUint = MemoryMarshal.Cast<byte, uint>(input);
|
||||
|
||||
int i = 0;
|
||||
|
||||
for (; i < inputUint.Length; i++)
|
||||
{
|
||||
uint depthStencil = inputUint[i];
|
||||
uint depth = depthStencil >> 8;
|
||||
uint stencil = depthStencil & 0xff;
|
||||
|
||||
int j = i * 2;
|
||||
|
||||
outputUint[j] = (uint)BitConverter.SingleToInt32Bits(depth * UnormToFloat);
|
||||
outputUint[j + 1] = stencil;
|
||||
}
|
||||
}
|
||||
|
||||
public static void ConvertD32FS8ToD24S8(Span<byte> output, ReadOnlySpan<byte> input)
|
||||
{
|
||||
Span<uint> outputUint = MemoryMarshal.Cast<byte, uint>(output);
|
||||
ReadOnlySpan<uint> inputUint = MemoryMarshal.Cast<byte, uint>(input);
|
||||
|
||||
int i = 0;
|
||||
|
||||
for (; i < inputUint.Length; i += 2)
|
||||
{
|
||||
float depth = BitConverter.Int32BitsToSingle((int)inputUint[i]);
|
||||
uint stencil = inputUint[i + 1];
|
||||
uint depthStencil = (Math.Clamp((uint)(depth * 0xffffff), 0, 0xffffff) << 8) | (stencil & 0xff);
|
||||
|
||||
int j = i >> 1;
|
||||
|
||||
outputUint[j] = depthStencil;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,196 +0,0 @@
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using SharpMetal.Metal;
|
||||
using System;
|
||||
using System.Runtime.Versioning;
|
||||
|
||||
namespace Ryujinx.Graphics.Metal
|
||||
{
|
||||
[SupportedOSPlatform("macos")]
|
||||
static class FormatTable
|
||||
{
|
||||
private static readonly MTLPixelFormat[] _table;
|
||||
|
||||
static FormatTable()
|
||||
{
|
||||
_table = new MTLPixelFormat[Enum.GetNames(typeof(Format)).Length];
|
||||
|
||||
Add(Format.R8Unorm, MTLPixelFormat.R8Unorm);
|
||||
Add(Format.R8Snorm, MTLPixelFormat.R8Snorm);
|
||||
Add(Format.R8Uint, MTLPixelFormat.R8Uint);
|
||||
Add(Format.R8Sint, MTLPixelFormat.R8Sint);
|
||||
Add(Format.R16Float, MTLPixelFormat.R16Float);
|
||||
Add(Format.R16Unorm, MTLPixelFormat.R16Unorm);
|
||||
Add(Format.R16Snorm, MTLPixelFormat.R16Snorm);
|
||||
Add(Format.R16Uint, MTLPixelFormat.R16Uint);
|
||||
Add(Format.R16Sint, MTLPixelFormat.R16Sint);
|
||||
Add(Format.R32Float, MTLPixelFormat.R32Float);
|
||||
Add(Format.R32Uint, MTLPixelFormat.R32Uint);
|
||||
Add(Format.R32Sint, MTLPixelFormat.R32Sint);
|
||||
Add(Format.R8G8Unorm, MTLPixelFormat.RG8Unorm);
|
||||
Add(Format.R8G8Snorm, MTLPixelFormat.RG8Snorm);
|
||||
Add(Format.R8G8Uint, MTLPixelFormat.RG8Uint);
|
||||
Add(Format.R8G8Sint, MTLPixelFormat.RG8Sint);
|
||||
Add(Format.R16G16Float, MTLPixelFormat.RG16Float);
|
||||
Add(Format.R16G16Unorm, MTLPixelFormat.RG16Unorm);
|
||||
Add(Format.R16G16Snorm, MTLPixelFormat.RG16Snorm);
|
||||
Add(Format.R16G16Uint, MTLPixelFormat.RG16Uint);
|
||||
Add(Format.R16G16Sint, MTLPixelFormat.RG16Sint);
|
||||
Add(Format.R32G32Float, MTLPixelFormat.RG32Float);
|
||||
Add(Format.R32G32Uint, MTLPixelFormat.RG32Uint);
|
||||
Add(Format.R32G32Sint, MTLPixelFormat.RG32Sint);
|
||||
// Add(Format.R8G8B8Unorm, MTLPixelFormat.R8G8B8Unorm);
|
||||
// Add(Format.R8G8B8Snorm, MTLPixelFormat.R8G8B8Snorm);
|
||||
// Add(Format.R8G8B8Uint, MTLPixelFormat.R8G8B8Uint);
|
||||
// Add(Format.R8G8B8Sint, MTLPixelFormat.R8G8B8Sint);
|
||||
// Add(Format.R16G16B16Float, MTLPixelFormat.R16G16B16Float);
|
||||
// Add(Format.R16G16B16Unorm, MTLPixelFormat.R16G16B16Unorm);
|
||||
// Add(Format.R16G16B16Snorm, MTLPixelFormat.R16G16B16SNorm);
|
||||
// Add(Format.R16G16B16Uint, MTLPixelFormat.R16G16B16Uint);
|
||||
// Add(Format.R16G16B16Sint, MTLPixelFormat.R16G16B16Sint);
|
||||
// Add(Format.R32G32B32Float, MTLPixelFormat.R32G32B32Sfloat);
|
||||
// Add(Format.R32G32B32Uint, MTLPixelFormat.R32G32B32Uint);
|
||||
// Add(Format.R32G32B32Sint, MTLPixelFormat.R32G32B32Sint);
|
||||
Add(Format.R8G8B8A8Unorm, MTLPixelFormat.RGBA8Unorm);
|
||||
Add(Format.R8G8B8A8Snorm, MTLPixelFormat.RGBA8Snorm);
|
||||
Add(Format.R8G8B8A8Uint, MTLPixelFormat.RGBA8Uint);
|
||||
Add(Format.R8G8B8A8Sint, MTLPixelFormat.RGBA8Sint);
|
||||
Add(Format.R16G16B16A16Float, MTLPixelFormat.RGBA16Float);
|
||||
Add(Format.R16G16B16A16Unorm, MTLPixelFormat.RGBA16Unorm);
|
||||
Add(Format.R16G16B16A16Snorm, MTLPixelFormat.RGBA16Snorm);
|
||||
Add(Format.R16G16B16A16Uint, MTLPixelFormat.RGBA16Uint);
|
||||
Add(Format.R16G16B16A16Sint, MTLPixelFormat.RGBA16Sint);
|
||||
Add(Format.R32G32B32A32Float, MTLPixelFormat.RGBA32Float);
|
||||
Add(Format.R32G32B32A32Uint, MTLPixelFormat.RGBA32Uint);
|
||||
Add(Format.R32G32B32A32Sint, MTLPixelFormat.RGBA32Sint);
|
||||
Add(Format.S8Uint, MTLPixelFormat.Stencil8);
|
||||
Add(Format.D16Unorm, MTLPixelFormat.Depth16Unorm);
|
||||
Add(Format.S8UintD24Unorm, MTLPixelFormat.Depth24UnormStencil8);
|
||||
Add(Format.X8UintD24Unorm, MTLPixelFormat.Depth24UnormStencil8);
|
||||
Add(Format.D32Float, MTLPixelFormat.Depth32Float);
|
||||
Add(Format.D24UnormS8Uint, MTLPixelFormat.Depth24UnormStencil8);
|
||||
Add(Format.D32FloatS8Uint, MTLPixelFormat.Depth32FloatStencil8);
|
||||
Add(Format.R8G8B8A8Srgb, MTLPixelFormat.RGBA8UnormsRGB);
|
||||
// Add(Format.R4G4Unorm, MTLPixelFormat.R4G4Unorm);
|
||||
Add(Format.R4G4B4A4Unorm, MTLPixelFormat.RGBA8Unorm);
|
||||
// Add(Format.R5G5B5X1Unorm, MTLPixelFormat.R5G5B5X1Unorm);
|
||||
Add(Format.R5G5B5A1Unorm, MTLPixelFormat.BGR5A1Unorm);
|
||||
Add(Format.R5G6B5Unorm, MTLPixelFormat.B5G6R5Unorm);
|
||||
Add(Format.R10G10B10A2Unorm, MTLPixelFormat.RGB10A2Unorm);
|
||||
Add(Format.R10G10B10A2Uint, MTLPixelFormat.RGB10A2Uint);
|
||||
Add(Format.R11G11B10Float, MTLPixelFormat.RG11B10Float);
|
||||
Add(Format.R9G9B9E5Float, MTLPixelFormat.RGB9E5Float);
|
||||
Add(Format.Bc1RgbaUnorm, MTLPixelFormat.BC1RGBA);
|
||||
Add(Format.Bc2Unorm, MTLPixelFormat.BC2RGBA);
|
||||
Add(Format.Bc3Unorm, MTLPixelFormat.BC3RGBA);
|
||||
Add(Format.Bc1RgbaSrgb, MTLPixelFormat.BC1RGBAsRGB);
|
||||
Add(Format.Bc2Srgb, MTLPixelFormat.BC2RGBAsRGB);
|
||||
Add(Format.Bc3Srgb, MTLPixelFormat.BC3RGBAsRGB);
|
||||
Add(Format.Bc4Unorm, MTLPixelFormat.BC4RUnorm);
|
||||
Add(Format.Bc4Snorm, MTLPixelFormat.BC4RSnorm);
|
||||
Add(Format.Bc5Unorm, MTLPixelFormat.BC5RGUnorm);
|
||||
Add(Format.Bc5Snorm, MTLPixelFormat.BC5RGSnorm);
|
||||
Add(Format.Bc7Unorm, MTLPixelFormat.BC7RGBAUnorm);
|
||||
Add(Format.Bc7Srgb, MTLPixelFormat.BC7RGBAUnormsRGB);
|
||||
Add(Format.Bc6HSfloat, MTLPixelFormat.BC6HRGBFloat);
|
||||
Add(Format.Bc6HUfloat, MTLPixelFormat.BC6HRGBUfloat);
|
||||
Add(Format.Etc2RgbUnorm, MTLPixelFormat.ETC2RGB8);
|
||||
// Add(Format.Etc2RgbaUnorm, MTLPixelFormat.ETC2RGBA8);
|
||||
Add(Format.Etc2RgbPtaUnorm, MTLPixelFormat.ETC2RGB8A1);
|
||||
Add(Format.Etc2RgbSrgb, MTLPixelFormat.ETC2RGB8sRGB);
|
||||
// Add(Format.Etc2RgbaSrgb, MTLPixelFormat.ETC2RGBA8sRGB);
|
||||
Add(Format.Etc2RgbPtaSrgb, MTLPixelFormat.ETC2RGB8A1sRGB);
|
||||
// Add(Format.R8Uscaled, MTLPixelFormat.R8Uscaled);
|
||||
// Add(Format.R8Sscaled, MTLPixelFormat.R8Sscaled);
|
||||
// Add(Format.R16Uscaled, MTLPixelFormat.R16Uscaled);
|
||||
// Add(Format.R16Sscaled, MTLPixelFormat.R16Sscaled);
|
||||
// Add(Format.R32Uscaled, MTLPixelFormat.R32Uscaled);
|
||||
// Add(Format.R32Sscaled, MTLPixelFormat.R32Sscaled);
|
||||
// Add(Format.R8G8Uscaled, MTLPixelFormat.R8G8Uscaled);
|
||||
// Add(Format.R8G8Sscaled, MTLPixelFormat.R8G8Sscaled);
|
||||
// Add(Format.R16G16Uscaled, MTLPixelFormat.R16G16Uscaled);
|
||||
// Add(Format.R16G16Sscaled, MTLPixelFormat.R16G16Sscaled);
|
||||
// Add(Format.R32G32Uscaled, MTLPixelFormat.R32G32Uscaled);
|
||||
// Add(Format.R32G32Sscaled, MTLPixelFormat.R32G32Sscaled);
|
||||
// Add(Format.R8G8B8Uscaled, MTLPixelFormat.R8G8B8Uscaled);
|
||||
// Add(Format.R8G8B8Sscaled, MTLPixelFormat.R8G8B8Sscaled);
|
||||
// Add(Format.R16G16B16Uscaled, MTLPixelFormat.R16G16B16Uscaled);
|
||||
// Add(Format.R16G16B16Sscaled, MTLPixelFormat.R16G16B16Sscaled);
|
||||
// Add(Format.R32G32B32Uscaled, MTLPixelFormat.R32G32B32Uscaled);
|
||||
// Add(Format.R32G32B32Sscaled, MTLPixelFormat.R32G32B32Sscaled);
|
||||
// Add(Format.R8G8B8A8Uscaled, MTLPixelFormat.R8G8B8A8Uscaled);
|
||||
// Add(Format.R8G8B8A8Sscaled, MTLPixelFormat.R8G8B8A8Sscaled);
|
||||
// Add(Format.R16G16B16A16Uscaled, MTLPixelFormat.R16G16B16A16Uscaled);
|
||||
// Add(Format.R16G16B16A16Sscaled, MTLPixelFormat.R16G16B16A16Sscaled);
|
||||
// Add(Format.R32G32B32A32Uscaled, MTLPixelFormat.R32G32B32A32Uscaled);
|
||||
// Add(Format.R32G32B32A32Sscaled, MTLPixelFormat.R32G32B32A32Sscaled);
|
||||
// Add(Format.R10G10B10A2Snorm, MTLPixelFormat.A2B10G10R10SNormPack32);
|
||||
// Add(Format.R10G10B10A2Sint, MTLPixelFormat.A2B10G10R10SintPack32);
|
||||
// Add(Format.R10G10B10A2Uscaled, MTLPixelFormat.A2B10G10R10UscaledPack32);
|
||||
// Add(Format.R10G10B10A2Sscaled, MTLPixelFormat.A2B10G10R10SscaledPack32);
|
||||
Add(Format.Astc4x4Unorm, MTLPixelFormat.ASTC4x4LDR);
|
||||
Add(Format.Astc5x4Unorm, MTLPixelFormat.ASTC5x4LDR);
|
||||
Add(Format.Astc5x5Unorm, MTLPixelFormat.ASTC5x5LDR);
|
||||
Add(Format.Astc6x5Unorm, MTLPixelFormat.ASTC6x5LDR);
|
||||
Add(Format.Astc6x6Unorm, MTLPixelFormat.ASTC6x6LDR);
|
||||
Add(Format.Astc8x5Unorm, MTLPixelFormat.ASTC8x5LDR);
|
||||
Add(Format.Astc8x6Unorm, MTLPixelFormat.ASTC8x6LDR);
|
||||
Add(Format.Astc8x8Unorm, MTLPixelFormat.ASTC8x8LDR);
|
||||
Add(Format.Astc10x5Unorm, MTLPixelFormat.ASTC10x5LDR);
|
||||
Add(Format.Astc10x6Unorm, MTLPixelFormat.ASTC10x6LDR);
|
||||
Add(Format.Astc10x8Unorm, MTLPixelFormat.ASTC10x8LDR);
|
||||
Add(Format.Astc10x10Unorm, MTLPixelFormat.ASTC10x10LDR);
|
||||
Add(Format.Astc12x10Unorm, MTLPixelFormat.ASTC12x10LDR);
|
||||
Add(Format.Astc12x12Unorm, MTLPixelFormat.ASTC12x12LDR);
|
||||
Add(Format.Astc4x4Srgb, MTLPixelFormat.ASTC4x4sRGB);
|
||||
Add(Format.Astc5x4Srgb, MTLPixelFormat.ASTC5x4sRGB);
|
||||
Add(Format.Astc5x5Srgb, MTLPixelFormat.ASTC5x5sRGB);
|
||||
Add(Format.Astc6x5Srgb, MTLPixelFormat.ASTC6x5sRGB);
|
||||
Add(Format.Astc6x6Srgb, MTLPixelFormat.ASTC6x6sRGB);
|
||||
Add(Format.Astc8x5Srgb, MTLPixelFormat.ASTC8x5sRGB);
|
||||
Add(Format.Astc8x6Srgb, MTLPixelFormat.ASTC8x6sRGB);
|
||||
Add(Format.Astc8x8Srgb, MTLPixelFormat.ASTC8x8sRGB);
|
||||
Add(Format.Astc10x5Srgb, MTLPixelFormat.ASTC10x5sRGB);
|
||||
Add(Format.Astc10x6Srgb, MTLPixelFormat.ASTC10x6sRGB);
|
||||
Add(Format.Astc10x8Srgb, MTLPixelFormat.ASTC10x8sRGB);
|
||||
Add(Format.Astc10x10Srgb, MTLPixelFormat.ASTC10x10sRGB);
|
||||
Add(Format.Astc12x10Srgb, MTLPixelFormat.ASTC12x10sRGB);
|
||||
Add(Format.Astc12x12Srgb, MTLPixelFormat.ASTC12x12sRGB);
|
||||
Add(Format.B5G6R5Unorm, MTLPixelFormat.B5G6R5Unorm);
|
||||
Add(Format.B5G5R5A1Unorm, MTLPixelFormat.BGR5A1Unorm);
|
||||
Add(Format.A1B5G5R5Unorm, MTLPixelFormat.A1BGR5Unorm);
|
||||
Add(Format.B8G8R8A8Unorm, MTLPixelFormat.BGRA8Unorm);
|
||||
Add(Format.B8G8R8A8Srgb, MTLPixelFormat.BGRA8UnormsRGB);
|
||||
}
|
||||
|
||||
private static void Add(Format format, MTLPixelFormat mtlFormat)
|
||||
{
|
||||
_table[(int)format] = mtlFormat;
|
||||
}
|
||||
|
||||
public static MTLPixelFormat GetFormat(Format format)
|
||||
{
|
||||
MTLPixelFormat mtlFormat = _table[(int)format];
|
||||
|
||||
if (IsD24S8(format))
|
||||
{
|
||||
if (!MTLDevice.CreateSystemDefaultDevice().Depth24Stencil8PixelFormatSupported)
|
||||
{
|
||||
mtlFormat = MTLPixelFormat.Depth32FloatStencil8;
|
||||
}
|
||||
}
|
||||
|
||||
if (mtlFormat == MTLPixelFormat.Invalid)
|
||||
{
|
||||
Logger.Error?.PrintMsg(LogClass.Gpu, $"Format {format} is not supported by the host.");
|
||||
}
|
||||
|
||||
return mtlFormat;
|
||||
}
|
||||
|
||||
public static bool IsD24S8(Format format)
|
||||
{
|
||||
return format == Format.D24UnormS8Uint || format == Format.S8UintD24Unorm || format == Format.X8UintD24Unorm;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Graphics.Metal
|
||||
{
|
||||
static partial class HardwareInfoTools
|
||||
{
|
||||
|
||||
private readonly static IntPtr _kCFAllocatorDefault = IntPtr.Zero;
|
||||
private readonly static UInt32 _kCFStringEncodingASCII = 0x0600;
|
||||
private const string IOKit = "/System/Library/Frameworks/IOKit.framework/IOKit";
|
||||
private const string CoreFoundation = "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation";
|
||||
|
||||
[LibraryImport(IOKit, StringMarshalling = StringMarshalling.Utf8)]
|
||||
private static partial IntPtr IOServiceMatching(string name);
|
||||
|
||||
[LibraryImport(IOKit)]
|
||||
private static partial IntPtr IOServiceGetMatchingService(IntPtr mainPort, IntPtr matching);
|
||||
|
||||
[LibraryImport(IOKit)]
|
||||
private static partial IntPtr IORegistryEntryCreateCFProperty(IntPtr entry, IntPtr key, IntPtr allocator, UInt32 options);
|
||||
|
||||
[LibraryImport(CoreFoundation, StringMarshalling = StringMarshalling.Utf8)]
|
||||
private static partial IntPtr CFStringCreateWithCString(IntPtr allocator, string cString, UInt32 encoding);
|
||||
|
||||
[LibraryImport(CoreFoundation)]
|
||||
[return: MarshalAs(UnmanagedType.U1)]
|
||||
public static partial bool CFStringGetCString(IntPtr theString, IntPtr buffer, long bufferSizes, UInt32 encoding);
|
||||
|
||||
[LibraryImport(CoreFoundation)]
|
||||
public static partial IntPtr CFDataGetBytePtr(IntPtr theData);
|
||||
|
||||
static string GetNameFromId(uint id)
|
||||
{
|
||||
return id switch
|
||||
{
|
||||
0x1002 => "AMD",
|
||||
0x106B => "Apple",
|
||||
0x10DE => "NVIDIA",
|
||||
0x13B5 => "ARM",
|
||||
0x8086 => "Intel",
|
||||
_ => $"0x{id:X}"
|
||||
};
|
||||
}
|
||||
|
||||
public static string GetVendor()
|
||||
{
|
||||
IntPtr serviceDict = IOServiceMatching("IOGPU");
|
||||
IntPtr service = IOServiceGetMatchingService(IntPtr.Zero, serviceDict);
|
||||
IntPtr cfString = CFStringCreateWithCString(_kCFAllocatorDefault, "vendor-id", _kCFStringEncodingASCII);
|
||||
IntPtr cfProperty = IORegistryEntryCreateCFProperty(service, cfString, _kCFAllocatorDefault, 0);
|
||||
|
||||
byte[] buffer = new byte[4];
|
||||
IntPtr bufferPtr = CFDataGetBytePtr(cfProperty);
|
||||
Marshal.Copy(bufferPtr, buffer, 0, buffer.Length);
|
||||
|
||||
uint vendorId = BitConverter.ToUInt32(buffer);
|
||||
|
||||
return GetNameFromId(vendorId);
|
||||
}
|
||||
|
||||
public static string GetModel()
|
||||
{
|
||||
IntPtr serviceDict = IOServiceMatching("IOGPU");
|
||||
IntPtr service = IOServiceGetMatchingService(IntPtr.Zero, serviceDict);
|
||||
IntPtr cfString = CFStringCreateWithCString(_kCFAllocatorDefault, "model", _kCFStringEncodingASCII);
|
||||
IntPtr cfProperty = IORegistryEntryCreateCFProperty(service, cfString, _kCFAllocatorDefault, 0);
|
||||
|
||||
char[] buffer = new char[64];
|
||||
IntPtr bufferPtr = Marshal.AllocHGlobal(buffer.Length);
|
||||
|
||||
if (CFStringGetCString(cfProperty, bufferPtr, buffer.Length, _kCFStringEncodingASCII))
|
||||
{
|
||||
string model = Marshal.PtrToStringUTF8(bufferPtr);
|
||||
Marshal.FreeHGlobal(bufferPtr);
|
||||
return model;
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,143 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Ryujinx.Graphics.Metal
|
||||
{
|
||||
interface IRefEquatable<T>
|
||||
{
|
||||
bool Equals(ref T other);
|
||||
}
|
||||
|
||||
class HashTableSlim<TKey, TValue> where TKey : IRefEquatable<TKey>
|
||||
{
|
||||
private const int TotalBuckets = 16; // Must be power of 2
|
||||
private const int TotalBucketsMask = TotalBuckets - 1;
|
||||
|
||||
private struct Entry
|
||||
{
|
||||
public int Hash;
|
||||
public TKey Key;
|
||||
public TValue Value;
|
||||
}
|
||||
|
||||
private struct Bucket
|
||||
{
|
||||
public int Length;
|
||||
public Entry[] Entries;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly Span<Entry> AsSpan()
|
||||
{
|
||||
return Entries == null ? Span<Entry>.Empty : Entries.AsSpan(0, Length);
|
||||
}
|
||||
}
|
||||
|
||||
private readonly Bucket[] _hashTable = new Bucket[TotalBuckets];
|
||||
|
||||
public IEnumerable<TKey> Keys
|
||||
{
|
||||
get
|
||||
{
|
||||
foreach (Bucket bucket in _hashTable)
|
||||
{
|
||||
for (int i = 0; i < bucket.Length; i++)
|
||||
{
|
||||
yield return bucket.Entries[i].Key;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<TValue> Values
|
||||
{
|
||||
get
|
||||
{
|
||||
foreach (Bucket bucket in _hashTable)
|
||||
{
|
||||
for (int i = 0; i < bucket.Length; i++)
|
||||
{
|
||||
yield return bucket.Entries[i].Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Add(ref TKey key, TValue value)
|
||||
{
|
||||
Entry entry = new()
|
||||
{
|
||||
Hash = key.GetHashCode(),
|
||||
Key = key,
|
||||
Value = value,
|
||||
};
|
||||
|
||||
int hashCode = key.GetHashCode();
|
||||
int bucketIndex = hashCode & TotalBucketsMask;
|
||||
|
||||
ref Bucket bucket = ref _hashTable[bucketIndex];
|
||||
if (bucket.Entries != null)
|
||||
{
|
||||
int index = bucket.Length;
|
||||
|
||||
if (index >= bucket.Entries.Length)
|
||||
{
|
||||
Array.Resize(ref bucket.Entries, index + 1);
|
||||
}
|
||||
|
||||
bucket.Entries[index] = entry;
|
||||
}
|
||||
else
|
||||
{
|
||||
bucket.Entries =
|
||||
[
|
||||
entry
|
||||
];
|
||||
}
|
||||
|
||||
bucket.Length++;
|
||||
}
|
||||
|
||||
public bool Remove(ref TKey key)
|
||||
{
|
||||
int hashCode = key.GetHashCode();
|
||||
|
||||
ref Bucket bucket = ref _hashTable[hashCode & TotalBucketsMask];
|
||||
Span<Entry> entries = bucket.AsSpan();
|
||||
for (int i = 0; i < entries.Length; i++)
|
||||
{
|
||||
ref Entry entry = ref entries[i];
|
||||
|
||||
if (entry.Hash == hashCode && entry.Key.Equals(ref key))
|
||||
{
|
||||
entries[(i + 1)..].CopyTo(entries[i..]);
|
||||
bucket.Length--;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TryGetValue(ref TKey key, out TValue value)
|
||||
{
|
||||
int hashCode = key.GetHashCode();
|
||||
|
||||
Span<Entry> entries = _hashTable[hashCode & TotalBucketsMask].AsSpan();
|
||||
for (int i = 0; i < entries.Length; i++)
|
||||
{
|
||||
ref Entry entry = ref entries[i];
|
||||
|
||||
if (entry.Hash == hashCode && entry.Key.Equals(ref key))
|
||||
{
|
||||
value = entry.Value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,868 +0,0 @@
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.Shader;
|
||||
using Ryujinx.Graphics.Shader.Translation;
|
||||
using SharpMetal.Metal;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.Versioning;
|
||||
|
||||
namespace Ryujinx.Graphics.Metal
|
||||
{
|
||||
[SupportedOSPlatform("macos")]
|
||||
class HelperShader : IDisposable
|
||||
{
|
||||
private const int ConvertElementsPerWorkgroup = 32 * 100; // Work group size of 32 times 100 elements.
|
||||
private const string ShadersSourcePath = "/Ryujinx.Graphics.Metal/Shaders";
|
||||
private readonly MetalRenderer _renderer;
|
||||
private readonly Pipeline _pipeline;
|
||||
private MTLDevice _device;
|
||||
|
||||
private readonly ISampler _samplerLinear;
|
||||
private readonly ISampler _samplerNearest;
|
||||
private readonly IProgram _programColorBlitF;
|
||||
private readonly IProgram _programColorBlitI;
|
||||
private readonly IProgram _programColorBlitU;
|
||||
private readonly IProgram _programColorBlitMsF;
|
||||
private readonly IProgram _programColorBlitMsI;
|
||||
private readonly IProgram _programColorBlitMsU;
|
||||
private readonly List<IProgram> _programsColorClearF = [];
|
||||
private readonly List<IProgram> _programsColorClearI = [];
|
||||
private readonly List<IProgram> _programsColorClearU = [];
|
||||
private readonly IProgram _programDepthStencilClear;
|
||||
private readonly IProgram _programStrideChange;
|
||||
private readonly IProgram _programConvertD32S8ToD24S8;
|
||||
private readonly IProgram _programConvertIndexBuffer;
|
||||
private readonly IProgram _programDepthBlit;
|
||||
private readonly IProgram _programDepthBlitMs;
|
||||
private readonly IProgram _programStencilBlit;
|
||||
private readonly IProgram _programStencilBlitMs;
|
||||
|
||||
private readonly EncoderState _helperShaderState = new();
|
||||
|
||||
public HelperShader(MTLDevice device, MetalRenderer renderer, Pipeline pipeline)
|
||||
{
|
||||
_device = device;
|
||||
_renderer = renderer;
|
||||
_pipeline = pipeline;
|
||||
|
||||
_samplerNearest = new SamplerHolder(renderer, _device, SamplerCreateInfo.Create(MinFilter.Nearest, MagFilter.Nearest));
|
||||
_samplerLinear = new SamplerHolder(renderer, _device, SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear));
|
||||
|
||||
ResourceLayout blitResourceLayout = new ResourceLayoutBuilder()
|
||||
.Add(ResourceStages.Vertex, ResourceType.UniformBuffer, 0)
|
||||
.Add(ResourceStages.Fragment, ResourceType.TextureAndSampler, 0).Build();
|
||||
|
||||
string blitSource = ReadMsl("Blit.metal");
|
||||
|
||||
string blitSourceF = blitSource.Replace("FORMAT", "float", StringComparison.Ordinal);
|
||||
_programColorBlitF = new Program(renderer, device, [
|
||||
new ShaderSource(blitSourceF, ShaderStage.Fragment, TargetLanguage.Msl),
|
||||
new ShaderSource(blitSourceF, ShaderStage.Vertex, TargetLanguage.Msl)
|
||||
], blitResourceLayout);
|
||||
|
||||
string blitSourceI = blitSource.Replace("FORMAT", "int");
|
||||
_programColorBlitI = new Program(renderer, device, [
|
||||
new ShaderSource(blitSourceI, ShaderStage.Fragment, TargetLanguage.Msl),
|
||||
new ShaderSource(blitSourceI, ShaderStage.Vertex, TargetLanguage.Msl)
|
||||
], blitResourceLayout);
|
||||
|
||||
string blitSourceU = blitSource.Replace("FORMAT", "uint");
|
||||
_programColorBlitU = new Program(renderer, device, [
|
||||
new ShaderSource(blitSourceU, ShaderStage.Fragment, TargetLanguage.Msl),
|
||||
new ShaderSource(blitSourceU, ShaderStage.Vertex, TargetLanguage.Msl)
|
||||
], blitResourceLayout);
|
||||
|
||||
string blitMsSource = ReadMsl("BlitMs.metal");
|
||||
|
||||
string blitMsSourceF = blitMsSource.Replace("FORMAT", "float");
|
||||
_programColorBlitMsF = new Program(renderer, device, [
|
||||
new ShaderSource(blitMsSourceF, ShaderStage.Fragment, TargetLanguage.Msl),
|
||||
new ShaderSource(blitMsSourceF, ShaderStage.Vertex, TargetLanguage.Msl)
|
||||
], blitResourceLayout);
|
||||
|
||||
string blitMsSourceI = blitMsSource.Replace("FORMAT", "int");
|
||||
_programColorBlitMsI = new Program(renderer, device, [
|
||||
new ShaderSource(blitMsSourceI, ShaderStage.Fragment, TargetLanguage.Msl),
|
||||
new ShaderSource(blitMsSourceI, ShaderStage.Vertex, TargetLanguage.Msl)
|
||||
], blitResourceLayout);
|
||||
|
||||
string blitMsSourceU = blitMsSource.Replace("FORMAT", "uint");
|
||||
_programColorBlitMsU = new Program(renderer, device, [
|
||||
new ShaderSource(blitMsSourceU, ShaderStage.Fragment, TargetLanguage.Msl),
|
||||
new ShaderSource(blitMsSourceU, ShaderStage.Vertex, TargetLanguage.Msl)
|
||||
], blitResourceLayout);
|
||||
|
||||
ResourceLayout colorClearResourceLayout = new ResourceLayoutBuilder()
|
||||
.Add(ResourceStages.Fragment, ResourceType.UniformBuffer, 0).Build();
|
||||
|
||||
string colorClearSource = ReadMsl("ColorClear.metal");
|
||||
|
||||
for (int i = 0; i < Constants.MaxColorAttachments; i++)
|
||||
{
|
||||
string crntSource = colorClearSource.Replace("COLOR_ATTACHMENT_INDEX", i.ToString()).Replace("FORMAT", "float");
|
||||
_programsColorClearF.Add(new Program(renderer, device, [
|
||||
new ShaderSource(crntSource, ShaderStage.Fragment, TargetLanguage.Msl),
|
||||
new ShaderSource(crntSource, ShaderStage.Vertex, TargetLanguage.Msl)
|
||||
], colorClearResourceLayout));
|
||||
}
|
||||
|
||||
for (int i = 0; i < Constants.MaxColorAttachments; i++)
|
||||
{
|
||||
string crntSource = colorClearSource.Replace("COLOR_ATTACHMENT_INDEX", i.ToString()).Replace("FORMAT", "int");
|
||||
_programsColorClearI.Add(new Program(renderer, device, [
|
||||
new ShaderSource(crntSource, ShaderStage.Fragment, TargetLanguage.Msl),
|
||||
new ShaderSource(crntSource, ShaderStage.Vertex, TargetLanguage.Msl)
|
||||
], colorClearResourceLayout));
|
||||
}
|
||||
|
||||
for (int i = 0; i < Constants.MaxColorAttachments; i++)
|
||||
{
|
||||
string crntSource = colorClearSource.Replace("COLOR_ATTACHMENT_INDEX", i.ToString()).Replace("FORMAT", "uint");
|
||||
_programsColorClearU.Add(new Program(renderer, device, [
|
||||
new ShaderSource(crntSource, ShaderStage.Fragment, TargetLanguage.Msl),
|
||||
new ShaderSource(crntSource, ShaderStage.Vertex, TargetLanguage.Msl)
|
||||
], colorClearResourceLayout));
|
||||
}
|
||||
|
||||
string depthStencilClearSource = ReadMsl("DepthStencilClear.metal");
|
||||
_programDepthStencilClear = new Program(renderer, device, [
|
||||
new ShaderSource(depthStencilClearSource, ShaderStage.Fragment, TargetLanguage.Msl),
|
||||
new ShaderSource(depthStencilClearSource, ShaderStage.Vertex, TargetLanguage.Msl)
|
||||
], colorClearResourceLayout);
|
||||
|
||||
ResourceLayout strideChangeResourceLayout = new ResourceLayoutBuilder()
|
||||
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
|
||||
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1)
|
||||
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2, true).Build();
|
||||
|
||||
string strideChangeSource = ReadMsl("ChangeBufferStride.metal");
|
||||
_programStrideChange = new Program(renderer, device, [
|
||||
new ShaderSource(strideChangeSource, ShaderStage.Compute, TargetLanguage.Msl)
|
||||
], strideChangeResourceLayout, new ComputeSize(64, 1, 1));
|
||||
|
||||
ResourceLayout convertD32S8ToD24S8ResourceLayout = new ResourceLayoutBuilder()
|
||||
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
|
||||
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1)
|
||||
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2, true).Build();
|
||||
|
||||
string convertD32S8ToD24S8Source = ReadMsl("ConvertD32S8ToD24S8.metal");
|
||||
_programConvertD32S8ToD24S8 = new Program(renderer, device, [
|
||||
new ShaderSource(convertD32S8ToD24S8Source, ShaderStage.Compute, TargetLanguage.Msl)
|
||||
], convertD32S8ToD24S8ResourceLayout, new ComputeSize(64, 1, 1));
|
||||
|
||||
ResourceLayout convertIndexBufferLayout = new ResourceLayoutBuilder()
|
||||
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1)
|
||||
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2, true)
|
||||
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 3).Build();
|
||||
|
||||
string convertIndexBufferSource = ReadMsl("ConvertIndexBuffer.metal");
|
||||
_programConvertIndexBuffer = new Program(renderer, device, [
|
||||
new ShaderSource(convertIndexBufferSource, ShaderStage.Compute, TargetLanguage.Msl)
|
||||
], convertIndexBufferLayout, new ComputeSize(16, 1, 1));
|
||||
|
||||
string depthBlitSource = ReadMsl("DepthBlit.metal");
|
||||
_programDepthBlit = new Program(renderer, device, [
|
||||
new ShaderSource(depthBlitSource, ShaderStage.Fragment, TargetLanguage.Msl),
|
||||
new ShaderSource(blitSourceF, ShaderStage.Vertex, TargetLanguage.Msl)
|
||||
], blitResourceLayout);
|
||||
|
||||
string depthBlitMsSource = ReadMsl("DepthBlitMs.metal");
|
||||
_programDepthBlitMs = new Program(renderer, device, [
|
||||
new ShaderSource(depthBlitMsSource, ShaderStage.Fragment, TargetLanguage.Msl),
|
||||
new ShaderSource(blitSourceF, ShaderStage.Vertex, TargetLanguage.Msl)
|
||||
], blitResourceLayout);
|
||||
|
||||
string stencilBlitSource = ReadMsl("StencilBlit.metal");
|
||||
_programStencilBlit = new Program(renderer, device, [
|
||||
new ShaderSource(stencilBlitSource, ShaderStage.Fragment, TargetLanguage.Msl),
|
||||
new ShaderSource(blitSourceF, ShaderStage.Vertex, TargetLanguage.Msl)
|
||||
], blitResourceLayout);
|
||||
|
||||
string stencilBlitMsSource = ReadMsl("StencilBlitMs.metal");
|
||||
_programStencilBlitMs = new Program(renderer, device, [
|
||||
new ShaderSource(stencilBlitMsSource, ShaderStage.Fragment, TargetLanguage.Msl),
|
||||
new ShaderSource(blitSourceF, ShaderStage.Vertex, TargetLanguage.Msl)
|
||||
], blitResourceLayout);
|
||||
}
|
||||
|
||||
private static string ReadMsl(string fileName)
|
||||
{
|
||||
string msl = EmbeddedResources.ReadAllText(string.Join('/', ShadersSourcePath, fileName));
|
||||
|
||||
#pragma warning disable IDE0055 // Disable formatting
|
||||
msl = msl.Replace("CONSTANT_BUFFERS_INDEX", $"{Constants.ConstantBuffersIndex}")
|
||||
.Replace("STORAGE_BUFFERS_INDEX", $"{Constants.StorageBuffersIndex}")
|
||||
.Replace("TEXTURES_INDEX", $"{Constants.TexturesIndex}")
|
||||
.Replace("IMAGES_INDEX", $"{Constants.ImagesIndex}");
|
||||
#pragma warning restore IDE0055
|
||||
|
||||
return msl;
|
||||
}
|
||||
|
||||
public unsafe void BlitColor(
|
||||
CommandBufferScoped cbs,
|
||||
Texture src,
|
||||
Texture dst,
|
||||
Extents2D srcRegion,
|
||||
Extents2D dstRegion,
|
||||
bool linearFilter,
|
||||
bool clear = false)
|
||||
{
|
||||
_pipeline.SwapState(_helperShaderState);
|
||||
|
||||
const int RegionBufferSize = 16;
|
||||
|
||||
ISampler sampler = linearFilter ? _samplerLinear : _samplerNearest;
|
||||
|
||||
_pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, src, sampler);
|
||||
|
||||
Span<float> region = stackalloc float[RegionBufferSize / sizeof(float)];
|
||||
|
||||
region[0] = srcRegion.X1 / (float)src.Width;
|
||||
region[1] = srcRegion.X2 / (float)src.Width;
|
||||
region[2] = srcRegion.Y1 / (float)src.Height;
|
||||
region[3] = srcRegion.Y2 / (float)src.Height;
|
||||
|
||||
if (dstRegion.X1 > dstRegion.X2)
|
||||
{
|
||||
(region[0], region[1]) = (region[1], region[0]);
|
||||
}
|
||||
|
||||
if (dstRegion.Y1 > dstRegion.Y2)
|
||||
{
|
||||
(region[2], region[3]) = (region[3], region[2]);
|
||||
}
|
||||
|
||||
using ScopedTemporaryBuffer buffer = _renderer.BufferManager.ReserveOrCreate(cbs, RegionBufferSize);
|
||||
buffer.Holder.SetDataUnchecked<float>(buffer.Offset, region);
|
||||
_pipeline.SetUniformBuffers([new BufferAssignment(0, buffer.Range)]);
|
||||
|
||||
Rectangle<float> rect = new(
|
||||
MathF.Min(dstRegion.X1, dstRegion.X2),
|
||||
MathF.Min(dstRegion.Y1, dstRegion.Y2),
|
||||
MathF.Abs(dstRegion.X2 - dstRegion.X1),
|
||||
MathF.Abs(dstRegion.Y2 - dstRegion.Y1));
|
||||
|
||||
Span<Viewport> viewports = stackalloc Viewport[16];
|
||||
|
||||
viewports[0] = new Viewport(
|
||||
rect,
|
||||
ViewportSwizzle.PositiveX,
|
||||
ViewportSwizzle.PositiveY,
|
||||
ViewportSwizzle.PositiveZ,
|
||||
ViewportSwizzle.PositiveW,
|
||||
0f,
|
||||
1f);
|
||||
|
||||
bool dstIsDepthOrStencil = dst.Info.Format.IsDepthOrStencil();
|
||||
|
||||
if (dstIsDepthOrStencil)
|
||||
{
|
||||
// TODO: Depth & stencil blit!
|
||||
Logger.Warning?.PrintMsg(LogClass.Gpu, "Requested a depth or stencil blit!");
|
||||
_pipeline.SwapState(null);
|
||||
return;
|
||||
}
|
||||
|
||||
string debugGroupName = "Blit Color ";
|
||||
|
||||
if (src.Info.Target.IsMultisample())
|
||||
{
|
||||
if (dst.Info.Format.IsSint())
|
||||
{
|
||||
debugGroupName += "MS Int";
|
||||
_pipeline.SetProgram(_programColorBlitMsI);
|
||||
}
|
||||
else if (dst.Info.Format.IsUint())
|
||||
{
|
||||
debugGroupName += "MS UInt";
|
||||
_pipeline.SetProgram(_programColorBlitMsU);
|
||||
}
|
||||
else
|
||||
{
|
||||
debugGroupName += "MS Float";
|
||||
_pipeline.SetProgram(_programColorBlitMsF);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (dst.Info.Format.IsSint())
|
||||
{
|
||||
debugGroupName += "Int";
|
||||
_pipeline.SetProgram(_programColorBlitI);
|
||||
}
|
||||
else if (dst.Info.Format.IsUint())
|
||||
{
|
||||
debugGroupName += "UInt";
|
||||
_pipeline.SetProgram(_programColorBlitU);
|
||||
}
|
||||
else
|
||||
{
|
||||
debugGroupName += "Float";
|
||||
_pipeline.SetProgram(_programColorBlitF);
|
||||
}
|
||||
}
|
||||
|
||||
int dstWidth = dst.Width;
|
||||
int dstHeight = dst.Height;
|
||||
|
||||
Span<Rectangle<int>> scissors = stackalloc Rectangle<int>[16];
|
||||
|
||||
scissors[0] = new Rectangle<int>(0, 0, dstWidth, dstHeight);
|
||||
|
||||
_pipeline.SetRenderTargets([dst], null);
|
||||
_pipeline.SetScissors(scissors);
|
||||
|
||||
_pipeline.SetClearLoadAction(clear);
|
||||
|
||||
_pipeline.SetViewports(viewports);
|
||||
_pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip);
|
||||
_pipeline.Draw(4, 1, 0, 0, debugGroupName);
|
||||
|
||||
// Cleanup
|
||||
if (clear)
|
||||
{
|
||||
_pipeline.SetClearLoadAction(false);
|
||||
}
|
||||
|
||||
// Restore previous state
|
||||
_pipeline.SwapState(null);
|
||||
}
|
||||
|
||||
public unsafe void BlitDepthStencil(
|
||||
CommandBufferScoped cbs,
|
||||
Texture src,
|
||||
Texture dst,
|
||||
Extents2D srcRegion,
|
||||
Extents2D dstRegion)
|
||||
{
|
||||
_pipeline.SwapState(_helperShaderState);
|
||||
|
||||
const int RegionBufferSize = 16;
|
||||
|
||||
Span<float> region = stackalloc float[RegionBufferSize / sizeof(float)];
|
||||
|
||||
region[0] = srcRegion.X1 / (float)src.Width;
|
||||
region[1] = srcRegion.X2 / (float)src.Width;
|
||||
region[2] = srcRegion.Y1 / (float)src.Height;
|
||||
region[3] = srcRegion.Y2 / (float)src.Height;
|
||||
|
||||
if (dstRegion.X1 > dstRegion.X2)
|
||||
{
|
||||
(region[0], region[1]) = (region[1], region[0]);
|
||||
}
|
||||
|
||||
if (dstRegion.Y1 > dstRegion.Y2)
|
||||
{
|
||||
(region[2], region[3]) = (region[3], region[2]);
|
||||
}
|
||||
|
||||
using ScopedTemporaryBuffer buffer = _renderer.BufferManager.ReserveOrCreate(cbs, RegionBufferSize);
|
||||
buffer.Holder.SetDataUnchecked<float>(buffer.Offset, region);
|
||||
_pipeline.SetUniformBuffers([new BufferAssignment(0, buffer.Range)]);
|
||||
|
||||
Span<Viewport> viewports = stackalloc Viewport[16];
|
||||
|
||||
Rectangle<float> rect = new(
|
||||
MathF.Min(dstRegion.X1, dstRegion.X2),
|
||||
MathF.Min(dstRegion.Y1, dstRegion.Y2),
|
||||
MathF.Abs(dstRegion.X2 - dstRegion.X1),
|
||||
MathF.Abs(dstRegion.Y2 - dstRegion.Y1));
|
||||
|
||||
viewports[0] = new Viewport(
|
||||
rect,
|
||||
ViewportSwizzle.PositiveX,
|
||||
ViewportSwizzle.PositiveY,
|
||||
ViewportSwizzle.PositiveZ,
|
||||
ViewportSwizzle.PositiveW,
|
||||
0f,
|
||||
1f);
|
||||
|
||||
int dstWidth = dst.Width;
|
||||
int dstHeight = dst.Height;
|
||||
|
||||
Span<Rectangle<int>> scissors = stackalloc Rectangle<int>[16];
|
||||
|
||||
scissors[0] = new Rectangle<int>(0, 0, dstWidth, dstHeight);
|
||||
|
||||
_pipeline.SetRenderTargets([], dst);
|
||||
_pipeline.SetScissors(scissors);
|
||||
_pipeline.SetViewports(viewports);
|
||||
_pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip);
|
||||
|
||||
if (src.Info.Format is
|
||||
Format.D16Unorm or
|
||||
Format.D32Float or
|
||||
Format.X8UintD24Unorm or
|
||||
Format.D24UnormS8Uint or
|
||||
Format.D32FloatS8Uint or
|
||||
Format.S8UintD24Unorm)
|
||||
{
|
||||
Texture depthTexture = CreateDepthOrStencilView(src, DepthStencilMode.Depth);
|
||||
|
||||
BlitDepthStencilDraw(depthTexture, isDepth: true);
|
||||
|
||||
if (depthTexture != src)
|
||||
{
|
||||
depthTexture.Release();
|
||||
}
|
||||
}
|
||||
|
||||
if (src.Info.Format is
|
||||
Format.S8Uint or
|
||||
Format.D24UnormS8Uint or
|
||||
Format.D32FloatS8Uint or
|
||||
Format.S8UintD24Unorm)
|
||||
{
|
||||
Texture stencilTexture = CreateDepthOrStencilView(src, DepthStencilMode.Stencil);
|
||||
|
||||
BlitDepthStencilDraw(stencilTexture, isDepth: false);
|
||||
|
||||
if (stencilTexture != src)
|
||||
{
|
||||
stencilTexture.Release();
|
||||
}
|
||||
}
|
||||
|
||||
// Restore previous state
|
||||
_pipeline.SwapState(null);
|
||||
}
|
||||
|
||||
private static Texture CreateDepthOrStencilView(Texture depthStencilTexture, DepthStencilMode depthStencilMode)
|
||||
{
|
||||
if (depthStencilTexture.Info.DepthStencilMode == depthStencilMode)
|
||||
{
|
||||
return depthStencilTexture;
|
||||
}
|
||||
|
||||
return (Texture)depthStencilTexture.CreateView(new TextureCreateInfo(
|
||||
depthStencilTexture.Info.Width,
|
||||
depthStencilTexture.Info.Height,
|
||||
depthStencilTexture.Info.Depth,
|
||||
depthStencilTexture.Info.Levels,
|
||||
depthStencilTexture.Info.Samples,
|
||||
depthStencilTexture.Info.BlockWidth,
|
||||
depthStencilTexture.Info.BlockHeight,
|
||||
depthStencilTexture.Info.BytesPerPixel,
|
||||
depthStencilTexture.Info.Format,
|
||||
depthStencilMode,
|
||||
depthStencilTexture.Info.Target,
|
||||
SwizzleComponent.Red,
|
||||
SwizzleComponent.Green,
|
||||
SwizzleComponent.Blue,
|
||||
SwizzleComponent.Alpha), 0, 0);
|
||||
}
|
||||
|
||||
private void BlitDepthStencilDraw(Texture src, bool isDepth)
|
||||
{
|
||||
// TODO: Check this https://github.com/Ryujinx/Ryujinx/pull/5003/
|
||||
_pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, src, _samplerNearest);
|
||||
|
||||
string debugGroupName;
|
||||
|
||||
if (isDepth)
|
||||
{
|
||||
debugGroupName = "Depth Blit";
|
||||
_pipeline.SetProgram(src.Info.Target.IsMultisample() ? _programDepthBlitMs : _programDepthBlit);
|
||||
_pipeline.SetDepthTest(new DepthTestDescriptor(true, true, CompareOp.Always));
|
||||
}
|
||||
else
|
||||
{
|
||||
debugGroupName = "Stencil Blit";
|
||||
_pipeline.SetProgram(src.Info.Target.IsMultisample() ? _programStencilBlitMs : _programStencilBlit);
|
||||
_pipeline.SetStencilTest(CreateStencilTestDescriptor(true));
|
||||
}
|
||||
|
||||
_pipeline.Draw(4, 1, 0, 0, debugGroupName);
|
||||
|
||||
if (isDepth)
|
||||
{
|
||||
_pipeline.SetDepthTest(new DepthTestDescriptor(false, false, CompareOp.Always));
|
||||
}
|
||||
else
|
||||
{
|
||||
_pipeline.SetStencilTest(CreateStencilTestDescriptor(false));
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe void DrawTexture(
|
||||
ITexture src,
|
||||
ISampler srcSampler,
|
||||
Extents2DF srcRegion,
|
||||
Extents2DF dstRegion)
|
||||
{
|
||||
// Save current state
|
||||
PredrawState state = _pipeline.SavePredrawState();
|
||||
|
||||
_pipeline.SetFaceCulling(false, Face.Front);
|
||||
_pipeline.SetStencilTest(new StencilTestDescriptor());
|
||||
_pipeline.SetDepthTest(new DepthTestDescriptor());
|
||||
|
||||
const int RegionBufferSize = 16;
|
||||
|
||||
_pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, src, srcSampler);
|
||||
|
||||
Span<float> region = stackalloc float[RegionBufferSize / sizeof(float)];
|
||||
|
||||
region[0] = srcRegion.X1 / src.Width;
|
||||
region[1] = srcRegion.X2 / src.Width;
|
||||
region[2] = srcRegion.Y1 / src.Height;
|
||||
region[3] = srcRegion.Y2 / src.Height;
|
||||
|
||||
if (dstRegion.X1 > dstRegion.X2)
|
||||
{
|
||||
(region[0], region[1]) = (region[1], region[0]);
|
||||
}
|
||||
|
||||
if (dstRegion.Y1 > dstRegion.Y2)
|
||||
{
|
||||
(region[2], region[3]) = (region[3], region[2]);
|
||||
}
|
||||
|
||||
BufferHandle bufferHandle = _renderer.BufferManager.CreateWithHandle(RegionBufferSize);
|
||||
_renderer.BufferManager.SetData<float>(bufferHandle, 0, region);
|
||||
_pipeline.SetUniformBuffers([new BufferAssignment(0, new BufferRange(bufferHandle, 0, RegionBufferSize))]);
|
||||
|
||||
Span<Viewport> viewports = stackalloc Viewport[16];
|
||||
|
||||
Rectangle<float> rect = new(
|
||||
MathF.Min(dstRegion.X1, dstRegion.X2),
|
||||
MathF.Min(dstRegion.Y1, dstRegion.Y2),
|
||||
MathF.Abs(dstRegion.X2 - dstRegion.X1),
|
||||
MathF.Abs(dstRegion.Y2 - dstRegion.Y1));
|
||||
|
||||
viewports[0] = new Viewport(
|
||||
rect,
|
||||
ViewportSwizzle.PositiveX,
|
||||
ViewportSwizzle.PositiveY,
|
||||
ViewportSwizzle.PositiveZ,
|
||||
ViewportSwizzle.PositiveW,
|
||||
0f,
|
||||
1f);
|
||||
|
||||
_pipeline.SetProgram(_programColorBlitF);
|
||||
_pipeline.SetViewports(viewports);
|
||||
_pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip);
|
||||
_pipeline.Draw(4, 1, 0, 0, "Draw Texture");
|
||||
|
||||
_renderer.BufferManager.Delete(bufferHandle);
|
||||
|
||||
// Restore previous state
|
||||
_pipeline.RestorePredrawState(state);
|
||||
}
|
||||
|
||||
public void ConvertI8ToI16(CommandBufferScoped cbs, BufferHolder src, BufferHolder dst, int srcOffset, int size)
|
||||
{
|
||||
ChangeStride(cbs, src, dst, srcOffset, size, 1, 2);
|
||||
}
|
||||
|
||||
public unsafe void ChangeStride(
|
||||
CommandBufferScoped cbs,
|
||||
BufferHolder src,
|
||||
BufferHolder dst,
|
||||
int srcOffset,
|
||||
int size,
|
||||
int stride,
|
||||
int newStride)
|
||||
{
|
||||
int elems = size / stride;
|
||||
|
||||
Auto<DisposableBuffer> srcBuffer = src.GetBuffer();
|
||||
Auto<DisposableBuffer> dstBuffer = dst.GetBuffer();
|
||||
|
||||
const int ParamsBufferSize = 4 * sizeof(int);
|
||||
|
||||
// Save current state
|
||||
_pipeline.SwapState(_helperShaderState);
|
||||
|
||||
Span<int> shaderParams = stackalloc int[ParamsBufferSize / sizeof(int)];
|
||||
|
||||
shaderParams[0] = stride;
|
||||
shaderParams[1] = newStride;
|
||||
shaderParams[2] = size;
|
||||
shaderParams[3] = srcOffset;
|
||||
|
||||
using ScopedTemporaryBuffer buffer = _renderer.BufferManager.ReserveOrCreate(cbs, ParamsBufferSize);
|
||||
buffer.Holder.SetDataUnchecked<int>(buffer.Offset, shaderParams);
|
||||
_pipeline.SetUniformBuffers([new BufferAssignment(0, buffer.Range)]);
|
||||
|
||||
Span<Auto<DisposableBuffer>> sbRanges = new Auto<DisposableBuffer>[2];
|
||||
|
||||
sbRanges[0] = srcBuffer;
|
||||
sbRanges[1] = dstBuffer;
|
||||
_pipeline.SetStorageBuffers(1, sbRanges);
|
||||
|
||||
_pipeline.SetProgram(_programStrideChange);
|
||||
_pipeline.DispatchCompute(1 + elems / ConvertElementsPerWorkgroup, 1, 1, "Change Stride");
|
||||
|
||||
// Restore previous state
|
||||
_pipeline.SwapState(null);
|
||||
}
|
||||
|
||||
public unsafe void ConvertD32S8ToD24S8(CommandBufferScoped cbs, BufferHolder src, Auto<DisposableBuffer> dstBuffer, int pixelCount, int dstOffset)
|
||||
{
|
||||
int inSize = pixelCount * 2 * sizeof(int);
|
||||
|
||||
Auto<DisposableBuffer> srcBuffer = src.GetBuffer();
|
||||
|
||||
const int ParamsBufferSize = sizeof(int) * 2;
|
||||
|
||||
// Save current state
|
||||
_pipeline.SwapState(_helperShaderState);
|
||||
|
||||
Span<int> shaderParams = stackalloc int[2];
|
||||
|
||||
shaderParams[0] = pixelCount;
|
||||
shaderParams[1] = dstOffset;
|
||||
|
||||
using ScopedTemporaryBuffer buffer = _renderer.BufferManager.ReserveOrCreate(cbs, ParamsBufferSize);
|
||||
buffer.Holder.SetDataUnchecked<int>(buffer.Offset, shaderParams);
|
||||
_pipeline.SetUniformBuffers([new BufferAssignment(0, buffer.Range)]);
|
||||
|
||||
Span<Auto<DisposableBuffer>> sbRanges = new Auto<DisposableBuffer>[2];
|
||||
|
||||
sbRanges[0] = srcBuffer;
|
||||
sbRanges[1] = dstBuffer;
|
||||
_pipeline.SetStorageBuffers(1, sbRanges);
|
||||
|
||||
_pipeline.SetProgram(_programConvertD32S8ToD24S8);
|
||||
_pipeline.DispatchCompute(1 + inSize / ConvertElementsPerWorkgroup, 1, 1, "D32S8 to D24S8 Conversion");
|
||||
|
||||
// Restore previous state
|
||||
_pipeline.SwapState(null);
|
||||
}
|
||||
|
||||
public void ConvertIndexBuffer(
|
||||
CommandBufferScoped cbs,
|
||||
BufferHolder src,
|
||||
BufferHolder dst,
|
||||
IndexBufferPattern pattern,
|
||||
int indexSize,
|
||||
int srcOffset,
|
||||
int indexCount)
|
||||
{
|
||||
// TODO: Support conversion with primitive restart enabled.
|
||||
|
||||
int primitiveCount = pattern.GetPrimitiveCount(indexCount);
|
||||
int outputIndexSize = 4;
|
||||
|
||||
Auto<DisposableBuffer> srcBuffer = src.GetBuffer();
|
||||
Auto<DisposableBuffer> dstBuffer = dst.GetBuffer();
|
||||
|
||||
const int ParamsBufferSize = 16 * sizeof(int);
|
||||
|
||||
// Save current state
|
||||
_pipeline.SwapState(_helperShaderState);
|
||||
|
||||
Span<int> shaderParams = stackalloc int[ParamsBufferSize / sizeof(int)];
|
||||
|
||||
shaderParams[8] = pattern.PrimitiveVertices;
|
||||
shaderParams[9] = pattern.PrimitiveVerticesOut;
|
||||
shaderParams[10] = indexSize;
|
||||
shaderParams[11] = outputIndexSize;
|
||||
shaderParams[12] = pattern.BaseIndex;
|
||||
shaderParams[13] = pattern.IndexStride;
|
||||
shaderParams[14] = srcOffset;
|
||||
shaderParams[15] = primitiveCount;
|
||||
|
||||
pattern.OffsetIndex.CopyTo(shaderParams[..pattern.OffsetIndex.Length]);
|
||||
|
||||
using ScopedTemporaryBuffer patternScoped = _renderer.BufferManager.ReserveOrCreate(cbs, ParamsBufferSize);
|
||||
patternScoped.Holder.SetDataUnchecked<int>(patternScoped.Offset, shaderParams);
|
||||
|
||||
Span<Auto<DisposableBuffer>> sbRanges = new Auto<DisposableBuffer>[2];
|
||||
|
||||
sbRanges[0] = srcBuffer;
|
||||
sbRanges[1] = dstBuffer;
|
||||
_pipeline.SetStorageBuffers(1, sbRanges);
|
||||
_pipeline.SetStorageBuffers([new BufferAssignment(3, patternScoped.Range)]);
|
||||
|
||||
_pipeline.SetProgram(_programConvertIndexBuffer);
|
||||
_pipeline.DispatchCompute(BitUtils.DivRoundUp(primitiveCount, 16), 1, 1, "Convert Index Buffer");
|
||||
|
||||
// Restore previous state
|
||||
_pipeline.SwapState(null);
|
||||
}
|
||||
|
||||
public unsafe void ClearColor(
|
||||
int index,
|
||||
ReadOnlySpan<float> clearColor,
|
||||
uint componentMask,
|
||||
int dstWidth,
|
||||
int dstHeight,
|
||||
Format format)
|
||||
{
|
||||
// Keep original scissor
|
||||
DirtyFlags clearFlags = DirtyFlags.All & (~DirtyFlags.Scissors);
|
||||
|
||||
// Save current state
|
||||
EncoderState originalState = _pipeline.SwapState(_helperShaderState, clearFlags, false);
|
||||
|
||||
// Inherit some state without fully recreating render pipeline.
|
||||
RenderTargetCopy save = _helperShaderState.InheritForClear(originalState, false, index);
|
||||
|
||||
const int ClearColorBufferSize = 16;
|
||||
|
||||
// TODO: Flush
|
||||
|
||||
using ScopedTemporaryBuffer buffer = _renderer.BufferManager.ReserveOrCreate(_pipeline.Cbs, ClearColorBufferSize);
|
||||
buffer.Holder.SetDataUnchecked(buffer.Offset, clearColor);
|
||||
_pipeline.SetUniformBuffers([new BufferAssignment(0, buffer.Range)]);
|
||||
|
||||
Span<Viewport> viewports = stackalloc Viewport[16];
|
||||
|
||||
// TODO: Set exact viewport!
|
||||
viewports[0] = new Viewport(
|
||||
new Rectangle<float>(0, 0, dstWidth, dstHeight),
|
||||
ViewportSwizzle.PositiveX,
|
||||
ViewportSwizzle.PositiveY,
|
||||
ViewportSwizzle.PositiveZ,
|
||||
ViewportSwizzle.PositiveW,
|
||||
0f,
|
||||
1f);
|
||||
|
||||
Span<uint> componentMasks = stackalloc uint[index + 1];
|
||||
componentMasks[index] = componentMask;
|
||||
|
||||
string debugGroupName = "Clear Color ";
|
||||
|
||||
if (format.IsSint())
|
||||
{
|
||||
debugGroupName += "Int";
|
||||
_pipeline.SetProgram(_programsColorClearI[index]);
|
||||
}
|
||||
else if (format.IsUint())
|
||||
{
|
||||
debugGroupName += "UInt";
|
||||
_pipeline.SetProgram(_programsColorClearU[index]);
|
||||
}
|
||||
else
|
||||
{
|
||||
debugGroupName += "Float";
|
||||
_pipeline.SetProgram(_programsColorClearF[index]);
|
||||
}
|
||||
|
||||
_pipeline.SetBlendState(index, new BlendDescriptor());
|
||||
_pipeline.SetFaceCulling(false, Face.Front);
|
||||
_pipeline.SetDepthTest(new DepthTestDescriptor(false, false, CompareOp.Always));
|
||||
_pipeline.SetRenderTargetColorMasks(componentMasks);
|
||||
_pipeline.SetViewports(viewports);
|
||||
_pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip);
|
||||
_pipeline.Draw(4, 1, 0, 0, debugGroupName);
|
||||
|
||||
// Restore previous state
|
||||
_pipeline.SwapState(null, clearFlags, false);
|
||||
|
||||
_helperShaderState.Restore(save);
|
||||
}
|
||||
|
||||
public unsafe void ClearDepthStencil(
|
||||
float depthValue,
|
||||
bool depthMask,
|
||||
int stencilValue,
|
||||
int stencilMask,
|
||||
int dstWidth,
|
||||
int dstHeight)
|
||||
{
|
||||
// Keep original scissor
|
||||
DirtyFlags clearFlags = DirtyFlags.All & (~DirtyFlags.Scissors);
|
||||
MTLScissorRect[] helperScissors = _helperShaderState.Scissors;
|
||||
|
||||
// Save current state
|
||||
EncoderState originalState = _pipeline.SwapState(_helperShaderState, clearFlags, false);
|
||||
|
||||
// Inherit some state without fully recreating render pipeline.
|
||||
RenderTargetCopy save = _helperShaderState.InheritForClear(originalState, true);
|
||||
|
||||
const int ClearDepthBufferSize = 16;
|
||||
|
||||
using ScopedTemporaryBuffer buffer = _renderer.BufferManager.ReserveOrCreate(_pipeline.Cbs, ClearDepthBufferSize);
|
||||
buffer.Holder.SetDataUnchecked(buffer.Offset, new ReadOnlySpan<float>(ref depthValue));
|
||||
_pipeline.SetUniformBuffers([new BufferAssignment(0, buffer.Range)]);
|
||||
|
||||
Span<Viewport> viewports = stackalloc Viewport[1];
|
||||
|
||||
viewports[0] = new Viewport(
|
||||
new Rectangle<float>(0, 0, dstWidth, dstHeight),
|
||||
ViewportSwizzle.PositiveX,
|
||||
ViewportSwizzle.PositiveY,
|
||||
ViewportSwizzle.PositiveZ,
|
||||
ViewportSwizzle.PositiveW,
|
||||
0f,
|
||||
1f);
|
||||
|
||||
_pipeline.SetProgram(_programDepthStencilClear);
|
||||
_pipeline.SetFaceCulling(false, Face.Front);
|
||||
_pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip);
|
||||
_pipeline.SetViewports(viewports);
|
||||
_pipeline.SetDepthTest(new DepthTestDescriptor(true, depthMask, CompareOp.Always));
|
||||
_pipeline.SetStencilTest(CreateStencilTestDescriptor(stencilMask != 0, stencilValue, 0xFF, stencilMask));
|
||||
_pipeline.Draw(4, 1, 0, 0, "Clear Depth Stencil");
|
||||
|
||||
// Cleanup
|
||||
_pipeline.SetDepthTest(new DepthTestDescriptor(false, false, CompareOp.Always));
|
||||
_pipeline.SetStencilTest(CreateStencilTestDescriptor(false));
|
||||
|
||||
// Restore previous state
|
||||
_pipeline.SwapState(null, clearFlags, false);
|
||||
|
||||
_helperShaderState.Restore(save);
|
||||
}
|
||||
|
||||
private static StencilTestDescriptor CreateStencilTestDescriptor(
|
||||
bool enabled,
|
||||
int refValue = 0,
|
||||
int compareMask = 0xff,
|
||||
int writeMask = 0xff)
|
||||
{
|
||||
return new StencilTestDescriptor(
|
||||
enabled,
|
||||
CompareOp.Always,
|
||||
StencilOp.Replace,
|
||||
StencilOp.Replace,
|
||||
StencilOp.Replace,
|
||||
refValue,
|
||||
compareMask,
|
||||
writeMask,
|
||||
CompareOp.Always,
|
||||
StencilOp.Replace,
|
||||
StencilOp.Replace,
|
||||
StencilOp.Replace,
|
||||
refValue,
|
||||
compareMask,
|
||||
writeMask);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_programColorBlitF.Dispose();
|
||||
_programColorBlitI.Dispose();
|
||||
_programColorBlitU.Dispose();
|
||||
_programColorBlitMsF.Dispose();
|
||||
_programColorBlitMsI.Dispose();
|
||||
_programColorBlitMsU.Dispose();
|
||||
|
||||
foreach (IProgram programColorClear in _programsColorClearF)
|
||||
{
|
||||
programColorClear.Dispose();
|
||||
}
|
||||
|
||||
foreach (IProgram programColorClear in _programsColorClearU)
|
||||
{
|
||||
programColorClear.Dispose();
|
||||
}
|
||||
|
||||
foreach (IProgram programColorClear in _programsColorClearI)
|
||||
{
|
||||
programColorClear.Dispose();
|
||||
}
|
||||
|
||||
_programDepthStencilClear.Dispose();
|
||||
_pipeline.Dispose();
|
||||
_samplerLinear.Dispose();
|
||||
_samplerNearest.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,121 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Graphics.Metal
|
||||
{
|
||||
class IdList<T> where T : class
|
||||
{
|
||||
private readonly List<T> _list;
|
||||
private int _freeMin;
|
||||
|
||||
public IdList()
|
||||
{
|
||||
_list = [];
|
||||
_freeMin = 0;
|
||||
}
|
||||
|
||||
public int Add(T value)
|
||||
{
|
||||
int id;
|
||||
int count = _list.Count;
|
||||
id = _list.IndexOf(null, _freeMin);
|
||||
|
||||
if ((uint)id < (uint)count)
|
||||
{
|
||||
_list[id] = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
id = count;
|
||||
_freeMin = id + 1;
|
||||
|
||||
_list.Add(value);
|
||||
}
|
||||
|
||||
return id + 1;
|
||||
}
|
||||
|
||||
public void Remove(int id)
|
||||
{
|
||||
id--;
|
||||
|
||||
int count = _list.Count;
|
||||
|
||||
if ((uint)id >= (uint)count)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (id + 1 == count)
|
||||
{
|
||||
// Trim unused items.
|
||||
int removeIndex = id;
|
||||
|
||||
while (removeIndex > 0 && _list[removeIndex - 1] == null)
|
||||
{
|
||||
removeIndex--;
|
||||
}
|
||||
|
||||
_list.RemoveRange(removeIndex, count - removeIndex);
|
||||
|
||||
if (_freeMin > removeIndex)
|
||||
{
|
||||
_freeMin = removeIndex;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_list[id] = null;
|
||||
|
||||
if (_freeMin > id)
|
||||
{
|
||||
_freeMin = id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryGetValue(int id, out T value)
|
||||
{
|
||||
id--;
|
||||
|
||||
try
|
||||
{
|
||||
if ((uint)id < (uint)_list.Count)
|
||||
{
|
||||
value = _list[id];
|
||||
return value != null;
|
||||
}
|
||||
|
||||
value = null;
|
||||
return false;
|
||||
}
|
||||
catch (ArgumentOutOfRangeException)
|
||||
{
|
||||
value = null;
|
||||
return false;
|
||||
}
|
||||
catch (IndexOutOfRangeException)
|
||||
{
|
||||
value = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
_list.Clear();
|
||||
_freeMin = 0;
|
||||
}
|
||||
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
for (int i = 0; i < _list.Count; i++)
|
||||
{
|
||||
if (_list[i] != null)
|
||||
{
|
||||
yield return _list[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using System.Runtime.Versioning;
|
||||
|
||||
namespace Ryujinx.Graphics.Metal
|
||||
{
|
||||
[SupportedOSPlatform("macos")]
|
||||
internal class ImageArray : IImageArray
|
||||
{
|
||||
private readonly TextureRef[] _textureRefs;
|
||||
private readonly TextureBuffer[] _bufferTextureRefs;
|
||||
|
||||
private readonly bool _isBuffer;
|
||||
private readonly Pipeline _pipeline;
|
||||
|
||||
public ImageArray(int size, bool isBuffer, Pipeline pipeline)
|
||||
{
|
||||
if (isBuffer)
|
||||
{
|
||||
_bufferTextureRefs = new TextureBuffer[size];
|
||||
}
|
||||
else
|
||||
{
|
||||
_textureRefs = new TextureRef[size];
|
||||
}
|
||||
|
||||
_isBuffer = isBuffer;
|
||||
_pipeline = pipeline;
|
||||
}
|
||||
|
||||
public void SetImages(int index, ITexture[] images)
|
||||
{
|
||||
for (int i = 0; i < images.Length; i++)
|
||||
{
|
||||
ITexture image = images[i];
|
||||
|
||||
if (image is TextureBuffer textureBuffer)
|
||||
{
|
||||
_bufferTextureRefs[index + i] = textureBuffer;
|
||||
}
|
||||
else if (image is Texture texture)
|
||||
{
|
||||
_textureRefs[index + i].Storage = texture;
|
||||
}
|
||||
else if (!_isBuffer)
|
||||
{
|
||||
_textureRefs[index + i].Storage = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
_bufferTextureRefs[index + i] = null;
|
||||
}
|
||||
}
|
||||
|
||||
SetDirty();
|
||||
}
|
||||
|
||||
public TextureRef[] GetTextureRefs()
|
||||
{
|
||||
return _textureRefs;
|
||||
}
|
||||
|
||||
public TextureBuffer[] GetBufferTextureRefs()
|
||||
{
|
||||
return _bufferTextureRefs;
|
||||
}
|
||||
|
||||
private void SetDirty()
|
||||
{
|
||||
_pipeline.DirtyImages();
|
||||
}
|
||||
|
||||
public void Dispose() { }
|
||||
}
|
||||
}
|
||||
@@ -1,118 +0,0 @@
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Versioning;
|
||||
|
||||
namespace Ryujinx.Graphics.Metal
|
||||
{
|
||||
[SupportedOSPlatform("macos")]
|
||||
internal class IndexBufferPattern : IDisposable
|
||||
{
|
||||
public int PrimitiveVertices { get; }
|
||||
public int PrimitiveVerticesOut { get; }
|
||||
public int BaseIndex { get; }
|
||||
public int[] OffsetIndex { get; }
|
||||
public int IndexStride { get; }
|
||||
public bool RepeatStart { get; }
|
||||
|
||||
private readonly MetalRenderer _renderer;
|
||||
private int _currentSize;
|
||||
private BufferHandle _repeatingBuffer;
|
||||
|
||||
public IndexBufferPattern(MetalRenderer renderer,
|
||||
int primitiveVertices,
|
||||
int primitiveVerticesOut,
|
||||
int baseIndex,
|
||||
int[] offsetIndex,
|
||||
int indexStride,
|
||||
bool repeatStart)
|
||||
{
|
||||
PrimitiveVertices = primitiveVertices;
|
||||
PrimitiveVerticesOut = primitiveVerticesOut;
|
||||
BaseIndex = baseIndex;
|
||||
OffsetIndex = offsetIndex;
|
||||
IndexStride = indexStride;
|
||||
RepeatStart = repeatStart;
|
||||
|
||||
_renderer = renderer;
|
||||
}
|
||||
|
||||
public int GetPrimitiveCount(int vertexCount)
|
||||
{
|
||||
return Math.Max(0, (vertexCount - BaseIndex) / IndexStride);
|
||||
}
|
||||
|
||||
public int GetConvertedCount(int indexCount)
|
||||
{
|
||||
int primitiveCount = GetPrimitiveCount(indexCount);
|
||||
return primitiveCount * OffsetIndex.Length;
|
||||
}
|
||||
|
||||
public BufferHandle GetRepeatingBuffer(int vertexCount, out int indexCount)
|
||||
{
|
||||
int primitiveCount = GetPrimitiveCount(vertexCount);
|
||||
indexCount = primitiveCount * PrimitiveVerticesOut;
|
||||
|
||||
int expectedSize = primitiveCount * OffsetIndex.Length;
|
||||
|
||||
if (expectedSize <= _currentSize && _repeatingBuffer != BufferHandle.Null)
|
||||
{
|
||||
return _repeatingBuffer;
|
||||
}
|
||||
|
||||
// Expand the repeating pattern to the number of requested primitives.
|
||||
BufferHandle newBuffer = _renderer.BufferManager.CreateWithHandle(expectedSize * sizeof(int));
|
||||
|
||||
// Copy the old data to the new one.
|
||||
if (_repeatingBuffer != BufferHandle.Null)
|
||||
{
|
||||
_renderer.Pipeline.CopyBuffer(_repeatingBuffer, newBuffer, 0, 0, _currentSize * sizeof(int));
|
||||
_renderer.BufferManager.Delete(_repeatingBuffer);
|
||||
}
|
||||
|
||||
_repeatingBuffer = newBuffer;
|
||||
|
||||
// Add the additional repeats on top.
|
||||
int newPrimitives = primitiveCount;
|
||||
int oldPrimitives = (_currentSize) / OffsetIndex.Length;
|
||||
|
||||
int[] newData;
|
||||
|
||||
newPrimitives -= oldPrimitives;
|
||||
newData = new int[expectedSize - _currentSize];
|
||||
|
||||
int outOffset = 0;
|
||||
int index = oldPrimitives * IndexStride + BaseIndex;
|
||||
|
||||
for (int i = 0; i < newPrimitives; i++)
|
||||
{
|
||||
if (RepeatStart)
|
||||
{
|
||||
// Used for triangle fan
|
||||
newData[outOffset++] = 0;
|
||||
}
|
||||
|
||||
for (int j = RepeatStart ? 1 : 0; j < OffsetIndex.Length; j++)
|
||||
{
|
||||
newData[outOffset++] = index + OffsetIndex[j];
|
||||
}
|
||||
|
||||
index += IndexStride;
|
||||
}
|
||||
|
||||
_renderer.SetBufferData(newBuffer, _currentSize * sizeof(int), MemoryMarshal.Cast<int, byte>(newData));
|
||||
_currentSize = expectedSize;
|
||||
|
||||
return newBuffer;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_repeatingBuffer != BufferHandle.Null)
|
||||
{
|
||||
_renderer.BufferManager.Delete(_repeatingBuffer);
|
||||
_repeatingBuffer = BufferHandle.Null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using SharpMetal.Metal;
|
||||
using System;
|
||||
using System.Runtime.Versioning;
|
||||
|
||||
namespace Ryujinx.Graphics.Metal
|
||||
{
|
||||
[SupportedOSPlatform("macos")]
|
||||
readonly internal struct IndexBufferState
|
||||
{
|
||||
public static IndexBufferState Null => new(BufferHandle.Null, 0, 0);
|
||||
|
||||
private readonly int _offset;
|
||||
private readonly int _size;
|
||||
private readonly IndexType _type;
|
||||
|
||||
private readonly BufferHandle _handle;
|
||||
|
||||
public IndexBufferState(BufferHandle handle, int offset, int size, IndexType type = IndexType.UInt)
|
||||
{
|
||||
_handle = handle;
|
||||
_offset = offset;
|
||||
_size = size;
|
||||
_type = type;
|
||||
}
|
||||
|
||||
public (MTLBuffer, int, MTLIndexType) GetIndexBuffer(MetalRenderer renderer, CommandBufferScoped cbs)
|
||||
{
|
||||
Auto<DisposableBuffer> autoBuffer;
|
||||
int offset, size;
|
||||
MTLIndexType type;
|
||||
|
||||
if (_type == IndexType.UByte)
|
||||
{
|
||||
// Index type is not supported. Convert to I16.
|
||||
autoBuffer = renderer.BufferManager.GetBufferI8ToI16(cbs, _handle, _offset, _size);
|
||||
|
||||
type = MTLIndexType.UInt16;
|
||||
offset = 0;
|
||||
size = _size * 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
autoBuffer = renderer.BufferManager.GetBuffer(_handle, false, out int bufferSize);
|
||||
|
||||
if (_offset >= bufferSize)
|
||||
{
|
||||
autoBuffer = null;
|
||||
}
|
||||
|
||||
type = _type.Convert();
|
||||
offset = _offset;
|
||||
size = _size;
|
||||
}
|
||||
|
||||
if (autoBuffer != null)
|
||||
{
|
||||
DisposableBuffer buffer = autoBuffer.Get(cbs, offset, size);
|
||||
|
||||
return (buffer.Value, offset, type);
|
||||
}
|
||||
|
||||
return (new MTLBuffer(IntPtr.Zero), 0, MTLIndexType.UInt16);
|
||||
}
|
||||
|
||||
public (MTLBuffer, int, MTLIndexType) GetConvertedIndexBuffer(
|
||||
MetalRenderer renderer,
|
||||
CommandBufferScoped cbs,
|
||||
int firstIndex,
|
||||
int indexCount,
|
||||
int convertedCount,
|
||||
IndexBufferPattern pattern)
|
||||
{
|
||||
// Convert the index buffer using the given pattern.
|
||||
int indexSize = GetIndexSize();
|
||||
|
||||
int firstIndexOffset = firstIndex * indexSize;
|
||||
|
||||
Auto<DisposableBuffer> autoBuffer = renderer.BufferManager.GetBufferTopologyConversion(cbs, _handle, _offset + firstIndexOffset, indexCount * indexSize, pattern, indexSize);
|
||||
|
||||
int size = convertedCount * 4;
|
||||
|
||||
if (autoBuffer != null)
|
||||
{
|
||||
DisposableBuffer buffer = autoBuffer.Get(cbs, 0, size);
|
||||
|
||||
return (buffer.Value, 0, MTLIndexType.UInt32);
|
||||
}
|
||||
|
||||
return (new MTLBuffer(IntPtr.Zero), 0, MTLIndexType.UInt32);
|
||||
}
|
||||
|
||||
private int GetIndexSize()
|
||||
{
|
||||
return _type switch
|
||||
{
|
||||
IndexType.UInt => 4,
|
||||
IndexType.UShort => 2,
|
||||
_ => 1,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,312 +0,0 @@
|
||||
using Ryujinx.Common.Configuration;
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.Shader.Translation;
|
||||
using SharpMetal.Metal;
|
||||
using SharpMetal.QuartzCore;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.Versioning;
|
||||
|
||||
namespace Ryujinx.Graphics.Metal
|
||||
{
|
||||
[SupportedOSPlatform("macos")]
|
||||
public sealed class MetalRenderer : IRenderer
|
||||
{
|
||||
public const int TotalSets = 4;
|
||||
|
||||
private readonly MTLDevice _device;
|
||||
private readonly MTLCommandQueue _queue;
|
||||
private readonly Func<CAMetalLayer> _getMetalLayer;
|
||||
|
||||
private Pipeline _pipeline;
|
||||
private Window _window;
|
||||
|
||||
public uint ProgramCount { get; set; }
|
||||
|
||||
#pragma warning disable CS0067 // The event is never used
|
||||
public event EventHandler<ScreenCaptureImageInfo> ScreenCaptured;
|
||||
#pragma warning restore CS0067
|
||||
|
||||
public bool PreferThreading => true;
|
||||
public IPipeline Pipeline => _pipeline;
|
||||
public IWindow Window => _window;
|
||||
|
||||
internal MTLCommandQueue BackgroundQueue { get; private set; }
|
||||
internal HelperShader HelperShader { get; private set; }
|
||||
internal BufferManager BufferManager { get; private set; }
|
||||
internal CommandBufferPool CommandBufferPool { get; private set; }
|
||||
internal BackgroundResources BackgroundResources { get; private set; }
|
||||
internal Action<Action> InterruptAction { get; private set; }
|
||||
internal SyncManager SyncManager { get; private set; }
|
||||
|
||||
internal HashSet<Program> Programs { get; }
|
||||
internal HashSet<SamplerHolder> Samplers { get; }
|
||||
|
||||
public MetalRenderer(Func<CAMetalLayer> metalLayer)
|
||||
{
|
||||
_device = MTLDevice.CreateSystemDefaultDevice();
|
||||
Programs = [];
|
||||
Samplers = [];
|
||||
|
||||
if (_device.ArgumentBuffersSupport != MTLArgumentBuffersTier.Tier2)
|
||||
{
|
||||
throw new NotSupportedException("Metal backend requires Tier 2 Argument Buffer support.");
|
||||
}
|
||||
|
||||
_queue = _device.NewCommandQueue(CommandBufferPool.MaxCommandBuffers + 1);
|
||||
BackgroundQueue = _device.NewCommandQueue(CommandBufferPool.MaxCommandBuffers);
|
||||
|
||||
_getMetalLayer = metalLayer;
|
||||
}
|
||||
|
||||
public void Initialize(GraphicsDebugLevel logLevel)
|
||||
{
|
||||
CAMetalLayer layer = _getMetalLayer();
|
||||
layer.Device = _device;
|
||||
layer.FramebufferOnly = false;
|
||||
|
||||
CommandBufferPool = new CommandBufferPool(_queue);
|
||||
_window = new Window(this, layer);
|
||||
_pipeline = new Pipeline(_device, this);
|
||||
BufferManager = new BufferManager(_device, this, _pipeline);
|
||||
|
||||
_pipeline.InitEncoderStateManager(BufferManager);
|
||||
|
||||
BackgroundResources = new BackgroundResources(this);
|
||||
HelperShader = new HelperShader(_device, this, _pipeline);
|
||||
SyncManager = new SyncManager(this);
|
||||
}
|
||||
|
||||
public void BackgroundContextAction(Action action, bool alwaysBackground = false)
|
||||
{
|
||||
// GetData methods should be thread safe, so we can call this directly.
|
||||
// Texture copy (scaled) may also happen in here, so that should also be thread safe.
|
||||
|
||||
action();
|
||||
}
|
||||
|
||||
public BufferHandle CreateBuffer(int size, BufferAccess access)
|
||||
{
|
||||
return BufferManager.CreateWithHandle(size);
|
||||
}
|
||||
|
||||
public BufferHandle CreateBuffer(IntPtr pointer, int size)
|
||||
{
|
||||
return BufferManager.Create(pointer, size);
|
||||
}
|
||||
|
||||
public BufferHandle CreateBufferSparse(ReadOnlySpan<BufferRange> storageBuffers)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IImageArray CreateImageArray(int size, bool isBuffer)
|
||||
{
|
||||
return new ImageArray(size, isBuffer, _pipeline);
|
||||
}
|
||||
|
||||
public IProgram CreateProgram(ShaderSource[] shaders, ShaderInfo info)
|
||||
{
|
||||
ProgramCount++;
|
||||
return new Program(this, _device, shaders, info.ResourceLayout, info.ComputeLocalSize);
|
||||
}
|
||||
|
||||
public ISampler CreateSampler(SamplerCreateInfo info)
|
||||
{
|
||||
return new SamplerHolder(this, _device, info);
|
||||
}
|
||||
|
||||
public ITexture CreateTexture(TextureCreateInfo info)
|
||||
{
|
||||
if (info.Target == Target.TextureBuffer)
|
||||
{
|
||||
return new TextureBuffer(_device, this, _pipeline, info);
|
||||
}
|
||||
|
||||
return new Texture(_device, this, _pipeline, info);
|
||||
}
|
||||
|
||||
public ITextureArray CreateTextureArray(int size, bool isBuffer)
|
||||
{
|
||||
return new TextureArray(size, isBuffer, _pipeline);
|
||||
}
|
||||
|
||||
public bool PrepareHostMapping(IntPtr address, ulong size)
|
||||
{
|
||||
// TODO: Metal Host Mapping
|
||||
return false;
|
||||
}
|
||||
|
||||
public void CreateSync(ulong id, bool strict)
|
||||
{
|
||||
SyncManager.Create(id, strict);
|
||||
}
|
||||
|
||||
public void DeleteBuffer(BufferHandle buffer)
|
||||
{
|
||||
BufferManager.Delete(buffer);
|
||||
}
|
||||
|
||||
public PinnedSpan<byte> GetBufferData(BufferHandle buffer, int offset, int size)
|
||||
{
|
||||
return BufferManager.GetData(buffer, offset, size);
|
||||
}
|
||||
|
||||
public Capabilities GetCapabilities()
|
||||
{
|
||||
// TODO: Finalize these values
|
||||
return new Capabilities(
|
||||
api: TargetApi.Metal,
|
||||
vendorName: HardwareInfoTools.GetVendor(),
|
||||
SystemMemoryType.UnifiedMemory,
|
||||
hasFrontFacingBug: false,
|
||||
hasVectorIndexingBug: false,
|
||||
needsFragmentOutputSpecialization: true,
|
||||
reduceShaderPrecision: true,
|
||||
supportsAstcCompression: true,
|
||||
supportsBc123Compression: true,
|
||||
supportsBc45Compression: true,
|
||||
supportsBc67Compression: true,
|
||||
supportsEtc2Compression: true,
|
||||
supports3DTextureCompression: true,
|
||||
supportsBgraFormat: true,
|
||||
supportsR4G4Format: false,
|
||||
supportsR4G4B4A4Format: true,
|
||||
supportsScaledVertexFormats: false,
|
||||
supportsSnormBufferTextureFormat: true,
|
||||
supportsSparseBuffer: false,
|
||||
supports5BitComponentFormat: true,
|
||||
supportsBlendEquationAdvanced: false,
|
||||
supportsFragmentShaderInterlock: true,
|
||||
supportsFragmentShaderOrderingIntel: false,
|
||||
supportsGeometryShader: false,
|
||||
supportsGeometryShaderPassthrough: false,
|
||||
supportsTransformFeedback: false,
|
||||
supportsImageLoadFormatted: false,
|
||||
supportsLayerVertexTessellation: false,
|
||||
supportsMismatchingViewFormat: true,
|
||||
supportsCubemapView: true,
|
||||
supportsNonConstantTextureOffset: false,
|
||||
supportsQuads: false,
|
||||
supportsSeparateSampler: true,
|
||||
supportsShaderBallot: false,
|
||||
supportsShaderBarrierDivergence: false,
|
||||
supportsShaderFloat64: false,
|
||||
supportsTextureGatherOffsets: false,
|
||||
supportsTextureShadowLod: false,
|
||||
supportsVertexStoreAndAtomics: false,
|
||||
supportsViewportIndexVertexTessellation: false,
|
||||
supportsViewportMask: false,
|
||||
supportsViewportSwizzle: false,
|
||||
supportsIndirectParameters: true,
|
||||
supportsDepthClipControl: false,
|
||||
uniformBufferSetIndex: (int)Constants.ConstantBuffersSetIndex,
|
||||
storageBufferSetIndex: (int)Constants.StorageBuffersSetIndex,
|
||||
textureSetIndex: (int)Constants.TexturesSetIndex,
|
||||
imageSetIndex: (int)Constants.ImagesSetIndex,
|
||||
extraSetBaseIndex: TotalSets,
|
||||
maximumExtraSets: (int)Constants.MaximumExtraSets,
|
||||
maximumUniformBuffersPerStage: Constants.MaxUniformBuffersPerStage,
|
||||
maximumStorageBuffersPerStage: Constants.MaxStorageBuffersPerStage,
|
||||
maximumTexturesPerStage: Constants.MaxTexturesPerStage,
|
||||
maximumImagesPerStage: Constants.MaxImagesPerStage,
|
||||
maximumComputeSharedMemorySize: (int)_device.MaxThreadgroupMemoryLength,
|
||||
maximumSupportedAnisotropy: 16,
|
||||
shaderSubgroupSize: 256,
|
||||
storageBufferOffsetAlignment: 16,
|
||||
textureBufferOffsetAlignment: 16,
|
||||
gatherBiasPrecision: 0,
|
||||
maximumGpuMemory: 0
|
||||
);
|
||||
}
|
||||
|
||||
public ulong GetCurrentSync()
|
||||
{
|
||||
return SyncManager.GetCurrent();
|
||||
}
|
||||
|
||||
public HardwareInfo GetHardwareInfo()
|
||||
{
|
||||
return new HardwareInfo(HardwareInfoTools.GetVendor(), HardwareInfoTools.GetModel(), "Apple");
|
||||
}
|
||||
|
||||
public IProgram LoadProgramBinary(byte[] programBinary, bool hasFragmentShader, ShaderInfo info)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void SetBufferData(BufferHandle buffer, int offset, ReadOnlySpan<byte> data)
|
||||
{
|
||||
BufferManager.SetData(buffer, offset, data, _pipeline.Cbs);
|
||||
}
|
||||
|
||||
public void UpdateCounters()
|
||||
{
|
||||
// https://developer.apple.com/documentation/metal/gpu_counters_and_counter_sample_buffers/creating_a_counter_sample_buffer_to_store_a_gpu_s_counter_data_during_a_pass?language=objc
|
||||
}
|
||||
|
||||
public void PreFrame()
|
||||
{
|
||||
SyncManager.Cleanup();
|
||||
}
|
||||
|
||||
public ICounterEvent ReportCounter(CounterType type, EventHandler<ulong> resultHandler, float divisor, bool hostReserved)
|
||||
{
|
||||
// https://developer.apple.com/documentation/metal/gpu_counters_and_counter_sample_buffers/creating_a_counter_sample_buffer_to_store_a_gpu_s_counter_data_during_a_pass?language=objc
|
||||
CounterEvent counterEvent = new();
|
||||
resultHandler?.Invoke(counterEvent, type == CounterType.SamplesPassed ? (ulong)1 : 0);
|
||||
return counterEvent;
|
||||
}
|
||||
|
||||
public void ResetCounter(CounterType type)
|
||||
{
|
||||
// https://developer.apple.com/documentation/metal/gpu_counters_and_counter_sample_buffers/creating_a_counter_sample_buffer_to_store_a_gpu_s_counter_data_during_a_pass?language=objc
|
||||
}
|
||||
|
||||
public void WaitSync(ulong id)
|
||||
{
|
||||
SyncManager.Wait(id);
|
||||
}
|
||||
|
||||
public void FlushAllCommands()
|
||||
{
|
||||
_pipeline.FlushCommandsImpl();
|
||||
}
|
||||
|
||||
public void RegisterFlush()
|
||||
{
|
||||
SyncManager.RegisterFlush();
|
||||
|
||||
// Periodically free unused regions of the staging buffer to avoid doing it all at once.
|
||||
BufferManager.StagingBuffer.FreeCompleted();
|
||||
}
|
||||
|
||||
public void SetInterruptAction(Action<Action> interruptAction)
|
||||
{
|
||||
InterruptAction = interruptAction;
|
||||
}
|
||||
|
||||
public void Screenshot()
|
||||
{
|
||||
// TODO: Screenshots
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
BackgroundResources.Dispose();
|
||||
|
||||
foreach (Program program in Programs)
|
||||
{
|
||||
program.Dispose();
|
||||
}
|
||||
|
||||
foreach (SamplerHolder sampler in Samplers)
|
||||
{
|
||||
sampler.Dispose();
|
||||
}
|
||||
|
||||
_pipeline.Dispose();
|
||||
_window.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,262 +0,0 @@
|
||||
using SharpMetal.Metal;
|
||||
using System;
|
||||
using System.Runtime.Versioning;
|
||||
|
||||
namespace Ryujinx.Graphics.Metal
|
||||
{
|
||||
/// <summary>
|
||||
/// Holder for multiple host GPU fences.
|
||||
/// </summary>
|
||||
[SupportedOSPlatform("macos")]
|
||||
class MultiFenceHolder
|
||||
{
|
||||
private const int BufferUsageTrackingGranularity = 4096;
|
||||
|
||||
private readonly FenceHolder[] _fences;
|
||||
private readonly BufferUsageBitmap _bufferUsageBitmap;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the multiple fence holder.
|
||||
/// </summary>
|
||||
public MultiFenceHolder()
|
||||
{
|
||||
_fences = new FenceHolder[CommandBufferPool.MaxCommandBuffers];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the multiple fence holder, with a given buffer size in mind.
|
||||
/// </summary>
|
||||
/// <param name="size">Size of the buffer</param>
|
||||
public MultiFenceHolder(int size)
|
||||
{
|
||||
_fences = new FenceHolder[CommandBufferPool.MaxCommandBuffers];
|
||||
_bufferUsageBitmap = new BufferUsageBitmap(size, BufferUsageTrackingGranularity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds read/write buffer usage information to the uses list.
|
||||
/// </summary>
|
||||
/// <param name="cbIndex">Index of the command buffer where the buffer is used</param>
|
||||
/// <param name="offset">Offset of the buffer being used</param>
|
||||
/// <param name="size">Size of the buffer region being used, in bytes</param>
|
||||
/// <param name="write">Whether the access is a write or not</param>
|
||||
public void AddBufferUse(int cbIndex, int offset, int size, bool write)
|
||||
{
|
||||
_bufferUsageBitmap.Add(cbIndex, offset, size, false);
|
||||
|
||||
if (write)
|
||||
{
|
||||
_bufferUsageBitmap.Add(cbIndex, offset, size, true);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all buffer usage information for a given command buffer.
|
||||
/// </summary>
|
||||
/// <param name="cbIndex">Index of the command buffer where the buffer is used</param>
|
||||
public void RemoveBufferUses(int cbIndex)
|
||||
{
|
||||
_bufferUsageBitmap?.Clear(cbIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a given range of a buffer is being used by a command buffer still being processed by the GPU.
|
||||
/// </summary>
|
||||
/// <param name="cbIndex">Index of the command buffer where the buffer is used</param>
|
||||
/// <param name="offset">Offset of the buffer being used</param>
|
||||
/// <param name="size">Size of the buffer region being used, in bytes</param>
|
||||
/// <returns>True if in use, false otherwise</returns>
|
||||
public bool IsBufferRangeInUse(int cbIndex, int offset, int size)
|
||||
{
|
||||
return _bufferUsageBitmap.OverlapsWith(cbIndex, offset, size);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a given range of a buffer is being used by any command buffer still being processed by the GPU.
|
||||
/// </summary>
|
||||
/// <param name="offset">Offset of the buffer being used</param>
|
||||
/// <param name="size">Size of the buffer region being used, in bytes</param>
|
||||
/// <param name="write">True if only write usages should count</param>
|
||||
/// <returns>True if in use, false otherwise</returns>
|
||||
public bool IsBufferRangeInUse(int offset, int size, bool write)
|
||||
{
|
||||
return _bufferUsageBitmap.OverlapsWith(offset, size, write);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a fence to the holder.
|
||||
/// </summary>
|
||||
/// <param name="cbIndex">Command buffer index of the command buffer that owns the fence</param>
|
||||
/// <param name="fence">Fence to be added</param>
|
||||
/// <returns>True if the command buffer's previous fence value was null</returns>
|
||||
public bool AddFence(int cbIndex, FenceHolder fence)
|
||||
{
|
||||
ref FenceHolder fenceRef = ref _fences[cbIndex];
|
||||
|
||||
if (fenceRef == null)
|
||||
{
|
||||
fenceRef = fence;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a fence from the holder.
|
||||
/// </summary>
|
||||
/// <param name="cbIndex">Command buffer index of the command buffer that owns the fence</param>
|
||||
public void RemoveFence(int cbIndex)
|
||||
{
|
||||
_fences[cbIndex] = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if a fence referenced on the given command buffer.
|
||||
/// </summary>
|
||||
/// <param name="cbIndex">Index of the command buffer to check if it's used</param>
|
||||
/// <returns>True if referenced, false otherwise</returns>
|
||||
public bool HasFence(int cbIndex)
|
||||
{
|
||||
return _fences[cbIndex] != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wait until all the fences on the holder are signaled.
|
||||
/// </summary>
|
||||
public void WaitForFences()
|
||||
{
|
||||
WaitForFencesImpl(0, 0, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wait until all the fences on the holder with buffer uses overlapping the specified range are signaled.
|
||||
/// </summary>
|
||||
/// <param name="offset">Start offset of the buffer range</param>
|
||||
/// <param name="size">Size of the buffer range in bytes</param>
|
||||
public void WaitForFences(int offset, int size)
|
||||
{
|
||||
WaitForFencesImpl(offset, size, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wait until all the fences on the holder with buffer uses overlapping the specified range are signaled.
|
||||
/// </summary>
|
||||
|
||||
// TODO: Add a proper timeout!
|
||||
public bool WaitForFences(bool indefinite)
|
||||
{
|
||||
return WaitForFencesImpl(0, 0, indefinite);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wait until all the fences on the holder with buffer uses overlapping the specified range are signaled.
|
||||
/// </summary>
|
||||
/// <param name="offset">Start offset of the buffer range</param>
|
||||
/// <param name="size">Size of the buffer range in bytes</param>
|
||||
/// <param name="indefinite">Indicates if this should wait indefinitely</param>
|
||||
/// <returns>True if all fences were signaled before the timeout expired, false otherwise</returns>
|
||||
private bool WaitForFencesImpl(int offset, int size, bool indefinite)
|
||||
{
|
||||
Span<FenceHolder> fenceHolders = new FenceHolder[CommandBufferPool.MaxCommandBuffers];
|
||||
|
||||
int count = size != 0 ? GetOverlappingFences(fenceHolders, offset, size) : GetFences(fenceHolders);
|
||||
Span<MTLCommandBuffer> fences = stackalloc MTLCommandBuffer[count];
|
||||
|
||||
int fenceCount = 0;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
if (fenceHolders[i].TryGet(out MTLCommandBuffer fence))
|
||||
{
|
||||
fences[fenceCount] = fence;
|
||||
|
||||
if (fenceCount < i)
|
||||
{
|
||||
fenceHolders[fenceCount] = fenceHolders[i];
|
||||
}
|
||||
|
||||
fenceCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (fenceCount == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool signaled = true;
|
||||
|
||||
if (indefinite)
|
||||
{
|
||||
foreach (MTLCommandBuffer fence in fences)
|
||||
{
|
||||
fence.WaitUntilCompleted();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (MTLCommandBuffer fence in fences)
|
||||
{
|
||||
if (fence.Status != MTLCommandBufferStatus.Completed)
|
||||
{
|
||||
signaled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < fenceCount; i++)
|
||||
{
|
||||
fenceHolders[i].Put();
|
||||
}
|
||||
|
||||
return signaled;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets fences to wait for.
|
||||
/// </summary>
|
||||
/// <param name="storage">Span to store fences in</param>
|
||||
/// <returns>Number of fences placed in storage</returns>
|
||||
private int GetFences(Span<FenceHolder> storage)
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
for (int i = 0; i < _fences.Length; i++)
|
||||
{
|
||||
FenceHolder fence = _fences[i];
|
||||
|
||||
if (fence != null)
|
||||
{
|
||||
storage[count++] = fence;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets fences to wait for use of a given buffer region.
|
||||
/// </summary>
|
||||
/// <param name="storage">Span to store overlapping fences in</param>
|
||||
/// <param name="offset">Offset of the range</param>
|
||||
/// <param name="size">Size of the range in bytes</param>
|
||||
/// <returns>Number of fences for the specified region placed in storage</returns>
|
||||
private int GetOverlappingFences(Span<FenceHolder> storage, int offset, int size)
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
for (int i = 0; i < _fences.Length; i++)
|
||||
{
|
||||
FenceHolder fence = _fences[i];
|
||||
|
||||
if (fence != null && _bufferUsageBitmap.OverlapsWith(i, offset, size))
|
||||
{
|
||||
storage[count++] = fence;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using SharpMetal.Metal;
|
||||
using System;
|
||||
using System.Runtime.Versioning;
|
||||
|
||||
namespace Ryujinx.Graphics.Metal
|
||||
{
|
||||
[SupportedOSPlatform("macos")]
|
||||
internal class PersistentFlushBuffer : IDisposable
|
||||
{
|
||||
private readonly MetalRenderer _renderer;
|
||||
|
||||
private BufferHolder _flushStorage;
|
||||
|
||||
public PersistentFlushBuffer(MetalRenderer renderer)
|
||||
{
|
||||
_renderer = renderer;
|
||||
}
|
||||
|
||||
private BufferHolder ResizeIfNeeded(int size)
|
||||
{
|
||||
BufferHolder flushStorage = _flushStorage;
|
||||
|
||||
if (flushStorage == null || size > _flushStorage.Size)
|
||||
{
|
||||
flushStorage?.Dispose();
|
||||
|
||||
flushStorage = _renderer.BufferManager.Create(size);
|
||||
_flushStorage = flushStorage;
|
||||
}
|
||||
|
||||
return flushStorage;
|
||||
}
|
||||
|
||||
public Span<byte> GetBufferData(CommandBufferPool cbp, BufferHolder buffer, int offset, int size)
|
||||
{
|
||||
BufferHolder flushStorage = ResizeIfNeeded(size);
|
||||
Auto<DisposableBuffer> srcBuffer;
|
||||
|
||||
using (CommandBufferScoped cbs = cbp.Rent())
|
||||
{
|
||||
srcBuffer = buffer.GetBuffer();
|
||||
Auto<DisposableBuffer> dstBuffer = flushStorage.GetBuffer();
|
||||
|
||||
if (srcBuffer.TryIncrementReferenceCount())
|
||||
{
|
||||
BufferHolder.Copy(cbs, srcBuffer, dstBuffer, offset, 0, size, registerSrcUsage: false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Source buffer is no longer alive, don't copy anything to flush storage.
|
||||
srcBuffer = null;
|
||||
}
|
||||
}
|
||||
|
||||
flushStorage.WaitForFences();
|
||||
srcBuffer?.DecrementReferenceCount();
|
||||
return flushStorage.GetDataStorage(0, size);
|
||||
}
|
||||
|
||||
public Span<byte> GetTextureData(CommandBufferPool cbp, Texture view, int size)
|
||||
{
|
||||
TextureCreateInfo info = view.Info;
|
||||
|
||||
BufferHolder flushStorage = ResizeIfNeeded(size);
|
||||
|
||||
using (CommandBufferScoped cbs = cbp.Rent())
|
||||
{
|
||||
MTLBuffer buffer = flushStorage.GetBuffer().Get(cbs).Value;
|
||||
MTLTexture image = view.GetHandle();
|
||||
|
||||
view.CopyFromOrToBuffer(cbs, buffer, image, size, true, 0, 0, info.GetLayers(), info.Levels, singleSlice: false);
|
||||
}
|
||||
|
||||
flushStorage.WaitForFences();
|
||||
return flushStorage.GetDataStorage(0, size);
|
||||
}
|
||||
|
||||
public Span<byte> GetTextureData(CommandBufferPool cbp, Texture view, int size, int layer, int level)
|
||||
{
|
||||
BufferHolder flushStorage = ResizeIfNeeded(size);
|
||||
|
||||
using (CommandBufferScoped cbs = cbp.Rent())
|
||||
{
|
||||
MTLBuffer buffer = flushStorage.GetBuffer().Get(cbs).Value;
|
||||
MTLTexture image = view.GetHandle();
|
||||
|
||||
view.CopyFromOrToBuffer(cbs, buffer, image, size, true, layer, level, 1, 1, singleSlice: true);
|
||||
}
|
||||
|
||||
flushStorage.WaitForFences();
|
||||
return flushStorage.GetDataStorage(0, size);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_flushStorage.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,877 +0,0 @@
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.Shader;
|
||||
using SharpMetal.Foundation;
|
||||
using SharpMetal.Metal;
|
||||
using SharpMetal.QuartzCore;
|
||||
using System;
|
||||
using System.Runtime.Versioning;
|
||||
|
||||
namespace Ryujinx.Graphics.Metal
|
||||
{
|
||||
public enum EncoderType
|
||||
{
|
||||
Blit,
|
||||
Compute,
|
||||
Render,
|
||||
None
|
||||
}
|
||||
|
||||
[SupportedOSPlatform("macos")]
|
||||
class Pipeline : IPipeline, IEncoderFactory, IDisposable
|
||||
{
|
||||
private const ulong MinByteWeightForFlush = 256 * 1024 * 1024; // MiB
|
||||
|
||||
private readonly MTLDevice _device;
|
||||
private readonly MetalRenderer _renderer;
|
||||
private EncoderStateManager _encoderStateManager;
|
||||
private ulong _byteWeight;
|
||||
|
||||
public MTLCommandBuffer CommandBuffer;
|
||||
|
||||
public IndexBufferPattern QuadsToTrisPattern;
|
||||
public IndexBufferPattern TriFanToTrisPattern;
|
||||
|
||||
internal CommandBufferScoped? PreloadCbs { get; private set; }
|
||||
internal CommandBufferScoped Cbs { get; private set; }
|
||||
internal CommandBufferEncoder Encoders => Cbs.Encoders;
|
||||
internal EncoderType CurrentEncoderType => Encoders.CurrentEncoderType;
|
||||
|
||||
public Pipeline(MTLDevice device, MetalRenderer renderer)
|
||||
{
|
||||
_device = device;
|
||||
_renderer = renderer;
|
||||
|
||||
renderer.CommandBufferPool.Initialize(this);
|
||||
|
||||
CommandBuffer = (Cbs = _renderer.CommandBufferPool.Rent()).CommandBuffer;
|
||||
}
|
||||
|
||||
internal void InitEncoderStateManager(BufferManager bufferManager)
|
||||
{
|
||||
_encoderStateManager = new EncoderStateManager(_device, bufferManager, this);
|
||||
|
||||
QuadsToTrisPattern = new IndexBufferPattern(_renderer, 4, 6, 0, [0, 1, 2, 0, 2, 3], 4, false);
|
||||
TriFanToTrisPattern = new IndexBufferPattern(_renderer, 3, 3, 2, [int.MinValue, -1, 0], 1, true);
|
||||
}
|
||||
|
||||
public EncoderState SwapState(EncoderState state, DirtyFlags flags = DirtyFlags.All, bool endRenderPass = true)
|
||||
{
|
||||
if (endRenderPass && CurrentEncoderType == EncoderType.Render)
|
||||
{
|
||||
EndCurrentPass();
|
||||
}
|
||||
|
||||
return _encoderStateManager.SwapState(state, flags);
|
||||
}
|
||||
|
||||
public PredrawState SavePredrawState()
|
||||
{
|
||||
return _encoderStateManager.SavePredrawState();
|
||||
}
|
||||
|
||||
public void RestorePredrawState(PredrawState state)
|
||||
{
|
||||
_encoderStateManager.RestorePredrawState(state);
|
||||
}
|
||||
|
||||
public void SetClearLoadAction(bool clear)
|
||||
{
|
||||
_encoderStateManager.SetClearLoadAction(clear);
|
||||
}
|
||||
|
||||
public MTLRenderCommandEncoder GetOrCreateRenderEncoder(bool forDraw = false)
|
||||
{
|
||||
// Mark all state as dirty to ensure it is set on the new encoder
|
||||
if (Cbs.Encoders.CurrentEncoderType != EncoderType.Render)
|
||||
{
|
||||
_encoderStateManager.SignalRenderDirty();
|
||||
}
|
||||
|
||||
if (forDraw)
|
||||
{
|
||||
_encoderStateManager.RenderResourcesPrepass();
|
||||
}
|
||||
|
||||
MTLRenderCommandEncoder renderCommandEncoder = Cbs.Encoders.EnsureRenderEncoder();
|
||||
|
||||
if (forDraw)
|
||||
{
|
||||
_encoderStateManager.RebindRenderState(renderCommandEncoder);
|
||||
}
|
||||
|
||||
return renderCommandEncoder;
|
||||
}
|
||||
|
||||
public MTLBlitCommandEncoder GetOrCreateBlitEncoder()
|
||||
{
|
||||
return Cbs.Encoders.EnsureBlitEncoder();
|
||||
}
|
||||
|
||||
public MTLComputeCommandEncoder GetOrCreateComputeEncoder(bool forDispatch = false)
|
||||
{
|
||||
// Mark all state as dirty to ensure it is set on the new encoder
|
||||
if (Cbs.Encoders.CurrentEncoderType != EncoderType.Compute)
|
||||
{
|
||||
_encoderStateManager.SignalComputeDirty();
|
||||
}
|
||||
|
||||
if (forDispatch)
|
||||
{
|
||||
_encoderStateManager.ComputeResourcesPrepass();
|
||||
}
|
||||
|
||||
MTLComputeCommandEncoder computeCommandEncoder = Cbs.Encoders.EnsureComputeEncoder();
|
||||
|
||||
if (forDispatch)
|
||||
{
|
||||
_encoderStateManager.RebindComputeState(computeCommandEncoder);
|
||||
}
|
||||
|
||||
return computeCommandEncoder;
|
||||
}
|
||||
|
||||
public void EndCurrentPass()
|
||||
{
|
||||
Cbs.Encoders.EndCurrentPass();
|
||||
}
|
||||
|
||||
public MTLRenderCommandEncoder CreateRenderCommandEncoder()
|
||||
{
|
||||
return _encoderStateManager.CreateRenderCommandEncoder();
|
||||
}
|
||||
|
||||
public MTLComputeCommandEncoder CreateComputeCommandEncoder()
|
||||
{
|
||||
return _encoderStateManager.CreateComputeCommandEncoder();
|
||||
}
|
||||
|
||||
public void Present(CAMetalDrawable drawable, Texture src, Extents2D srcRegion, Extents2D dstRegion, bool isLinear)
|
||||
{
|
||||
// TODO: Clean this up
|
||||
TextureCreateInfo textureInfo = new((int)drawable.Texture.Width, (int)drawable.Texture.Height, (int)drawable.Texture.Depth, (int)drawable.Texture.MipmapLevelCount, (int)drawable.Texture.SampleCount, 0, 0, 0, Format.B8G8R8A8Unorm, 0, Target.Texture2D, SwizzleComponent.Red, SwizzleComponent.Green, SwizzleComponent.Blue, SwizzleComponent.Alpha);
|
||||
Texture dst = new(_device, _renderer, this, textureInfo, drawable.Texture, 0, 0);
|
||||
|
||||
_renderer.HelperShader.BlitColor(Cbs, src, dst, srcRegion, dstRegion, isLinear, true);
|
||||
|
||||
EndCurrentPass();
|
||||
|
||||
Cbs.CommandBuffer.PresentDrawable(drawable);
|
||||
|
||||
FlushCommandsImpl();
|
||||
|
||||
// TODO: Auto flush counting
|
||||
_renderer.SyncManager.GetAndResetWaitTicks();
|
||||
|
||||
// Cleanup
|
||||
dst.Dispose();
|
||||
}
|
||||
|
||||
public CommandBufferScoped GetPreloadCommandBuffer()
|
||||
{
|
||||
PreloadCbs ??= _renderer.CommandBufferPool.Rent();
|
||||
|
||||
return PreloadCbs.Value;
|
||||
}
|
||||
|
||||
public void FlushCommandsIfWeightExceeding(IAuto disposedResource, ulong byteWeight)
|
||||
{
|
||||
bool usedByCurrentCb = disposedResource.HasCommandBufferDependency(Cbs);
|
||||
|
||||
if (PreloadCbs != null && !usedByCurrentCb)
|
||||
{
|
||||
usedByCurrentCb = disposedResource.HasCommandBufferDependency(PreloadCbs.Value);
|
||||
}
|
||||
|
||||
if (usedByCurrentCb)
|
||||
{
|
||||
// Since we can only free memory after the command buffer that uses a given resource was executed,
|
||||
// keeping the command buffer might cause a high amount of memory to be in use.
|
||||
// To prevent that, we force submit command buffers if the memory usage by resources
|
||||
// in use by the current command buffer is above a given limit, and those resources were disposed.
|
||||
_byteWeight += byteWeight;
|
||||
|
||||
if (_byteWeight >= MinByteWeightForFlush)
|
||||
{
|
||||
FlushCommandsImpl();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void FlushCommandsImpl()
|
||||
{
|
||||
EndCurrentPass();
|
||||
|
||||
_byteWeight = 0;
|
||||
|
||||
if (PreloadCbs != null)
|
||||
{
|
||||
PreloadCbs.Value.Dispose();
|
||||
PreloadCbs = null;
|
||||
}
|
||||
|
||||
CommandBuffer = (Cbs = _renderer.CommandBufferPool.ReturnAndRent(Cbs)).CommandBuffer;
|
||||
_renderer.RegisterFlush();
|
||||
}
|
||||
|
||||
public void DirtyTextures()
|
||||
{
|
||||
_encoderStateManager.DirtyTextures();
|
||||
}
|
||||
|
||||
public void DirtyImages()
|
||||
{
|
||||
_encoderStateManager.DirtyImages();
|
||||
}
|
||||
|
||||
public void Blit(
|
||||
Texture src,
|
||||
Texture dst,
|
||||
Extents2D srcRegion,
|
||||
Extents2D dstRegion,
|
||||
bool isDepthOrStencil,
|
||||
bool linearFilter)
|
||||
{
|
||||
if (isDepthOrStencil)
|
||||
{
|
||||
_renderer.HelperShader.BlitDepthStencil(Cbs, src, dst, srcRegion, dstRegion);
|
||||
}
|
||||
else
|
||||
{
|
||||
_renderer.HelperShader.BlitColor(Cbs, src, dst, srcRegion, dstRegion, linearFilter);
|
||||
}
|
||||
}
|
||||
|
||||
public void Barrier()
|
||||
{
|
||||
switch (CurrentEncoderType)
|
||||
{
|
||||
case EncoderType.Render:
|
||||
{
|
||||
MTLBarrierScope scope = MTLBarrierScope.Buffers | MTLBarrierScope.Textures | MTLBarrierScope.RenderTargets;
|
||||
MTLRenderStages stages = MTLRenderStages.RenderStageVertex | MTLRenderStages.RenderStageFragment;
|
||||
Encoders.RenderEncoder.MemoryBarrier(scope, stages, stages);
|
||||
break;
|
||||
}
|
||||
case EncoderType.Compute:
|
||||
{
|
||||
MTLBarrierScope scope = MTLBarrierScope.Buffers | MTLBarrierScope.Textures | MTLBarrierScope.RenderTargets;
|
||||
Encoders.ComputeEncoder.MemoryBarrier(scope);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearBuffer(BufferHandle destination, int offset, int size, uint value)
|
||||
{
|
||||
MTLBlitCommandEncoder blitCommandEncoder = GetOrCreateBlitEncoder();
|
||||
|
||||
MTLBuffer mtlBuffer = _renderer.BufferManager.GetBuffer(destination, offset, size, true).Get(Cbs, offset, size, true).Value;
|
||||
|
||||
// Might need a closer look, range's count, lower, and upper bound
|
||||
// must be a multiple of 4
|
||||
blitCommandEncoder.FillBuffer(mtlBuffer,
|
||||
new NSRange
|
||||
{
|
||||
location = (ulong)offset,
|
||||
length = (ulong)size
|
||||
},
|
||||
(byte)value);
|
||||
}
|
||||
|
||||
public void ClearRenderTargetColor(int index, int layer, int layerCount, uint componentMask, ColorF color)
|
||||
{
|
||||
float[] colors = [color.Red, color.Green, color.Blue, color.Alpha];
|
||||
Texture dst = _encoderStateManager.RenderTargets[index];
|
||||
|
||||
// TODO: Remove workaround for Wonder which has an invalid texture due to unsupported format
|
||||
if (dst == null)
|
||||
{
|
||||
Logger.Warning?.PrintMsg(LogClass.Gpu, "Attempted to clear invalid render target!");
|
||||
return;
|
||||
}
|
||||
|
||||
_renderer.HelperShader.ClearColor(index, colors, componentMask, dst.Width, dst.Height, dst.Info.Format);
|
||||
}
|
||||
|
||||
public void ClearRenderTargetDepthStencil(int layer, int layerCount, float depthValue, bool depthMask, int stencilValue, int stencilMask)
|
||||
{
|
||||
Texture depthStencil = _encoderStateManager.DepthStencil;
|
||||
|
||||
if (depthStencil == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_renderer.HelperShader.ClearDepthStencil(depthValue, depthMask, stencilValue, stencilMask, depthStencil.Width, depthStencil.Height);
|
||||
}
|
||||
|
||||
public void CommandBufferBarrier()
|
||||
{
|
||||
Barrier();
|
||||
}
|
||||
|
||||
public void CopyBuffer(BufferHandle src, BufferHandle dst, int srcOffset, int dstOffset, int size)
|
||||
{
|
||||
Auto<DisposableBuffer> srcBuffer = _renderer.BufferManager.GetBuffer(src, srcOffset, size, false);
|
||||
Auto<DisposableBuffer> dstBuffer = _renderer.BufferManager.GetBuffer(dst, dstOffset, size, true);
|
||||
|
||||
BufferHolder.Copy(Cbs, srcBuffer, dstBuffer, srcOffset, dstOffset, size);
|
||||
}
|
||||
|
||||
public void PushDebugGroup(string name)
|
||||
{
|
||||
MTLCommandEncoder? encoder = Encoders.CurrentEncoder;
|
||||
NSString debugGroupName = StringHelper.NSString(name);
|
||||
|
||||
if (encoder == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (Encoders.CurrentEncoderType)
|
||||
{
|
||||
case EncoderType.Render:
|
||||
encoder.Value.PushDebugGroup(debugGroupName);
|
||||
break;
|
||||
case EncoderType.Blit:
|
||||
encoder.Value.PushDebugGroup(debugGroupName);
|
||||
break;
|
||||
case EncoderType.Compute:
|
||||
encoder.Value.PushDebugGroup(debugGroupName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void PopDebugGroup()
|
||||
{
|
||||
MTLCommandEncoder? encoder = Encoders.CurrentEncoder;
|
||||
|
||||
if (encoder == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (Encoders.CurrentEncoderType)
|
||||
{
|
||||
case EncoderType.Render:
|
||||
encoder.Value.PopDebugGroup();
|
||||
break;
|
||||
case EncoderType.Blit:
|
||||
encoder.Value.PopDebugGroup();
|
||||
break;
|
||||
case EncoderType.Compute:
|
||||
encoder.Value.PopDebugGroup();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void DispatchCompute(int groupsX, int groupsY, int groupsZ)
|
||||
{
|
||||
DispatchCompute(groupsX, groupsY, groupsZ, String.Empty);
|
||||
}
|
||||
|
||||
public void DispatchCompute(int groupsX, int groupsY, int groupsZ, string debugGroupName)
|
||||
{
|
||||
MTLComputeCommandEncoder computeCommandEncoder = GetOrCreateComputeEncoder(true);
|
||||
|
||||
ComputeSize localSize = _encoderStateManager.ComputeLocalSize;
|
||||
|
||||
if (debugGroupName != String.Empty)
|
||||
{
|
||||
PushDebugGroup(debugGroupName);
|
||||
}
|
||||
|
||||
computeCommandEncoder.DispatchThreadgroups(
|
||||
new MTLSize { width = (ulong)groupsX, height = (ulong)groupsY, depth = (ulong)groupsZ },
|
||||
new MTLSize { width = (ulong)localSize.X, height = (ulong)localSize.Y, depth = (ulong)localSize.Z });
|
||||
|
||||
if (debugGroupName != String.Empty)
|
||||
{
|
||||
PopDebugGroup();
|
||||
}
|
||||
}
|
||||
|
||||
public void Draw(int vertexCount, int instanceCount, int firstVertex, int firstInstance)
|
||||
{
|
||||
Draw(vertexCount, instanceCount, firstVertex, firstInstance, String.Empty);
|
||||
}
|
||||
|
||||
public void Draw(int vertexCount, int instanceCount, int firstVertex, int firstInstance, string debugGroupName)
|
||||
{
|
||||
if (vertexCount == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
MTLPrimitiveType primitiveType = TopologyRemap(_encoderStateManager.Topology).Convert();
|
||||
|
||||
if (TopologyUnsupported(_encoderStateManager.Topology))
|
||||
{
|
||||
IndexBufferPattern pattern = GetIndexBufferPattern();
|
||||
|
||||
BufferHandle handle = pattern.GetRepeatingBuffer(vertexCount, out int indexCount);
|
||||
Auto<DisposableBuffer> buffer = _renderer.BufferManager.GetBuffer(handle, false);
|
||||
MTLBuffer mtlBuffer = buffer.Get(Cbs, 0, indexCount * sizeof(int)).Value;
|
||||
|
||||
MTLRenderCommandEncoder renderCommandEncoder = GetOrCreateRenderEncoder(true);
|
||||
|
||||
renderCommandEncoder.DrawIndexedPrimitives(
|
||||
primitiveType,
|
||||
(ulong)indexCount,
|
||||
MTLIndexType.UInt32,
|
||||
mtlBuffer,
|
||||
0);
|
||||
}
|
||||
else
|
||||
{
|
||||
MTLRenderCommandEncoder renderCommandEncoder = GetOrCreateRenderEncoder(true);
|
||||
|
||||
if (debugGroupName != String.Empty)
|
||||
{
|
||||
PushDebugGroup(debugGroupName);
|
||||
}
|
||||
|
||||
renderCommandEncoder.DrawPrimitives(
|
||||
primitiveType,
|
||||
(ulong)firstVertex,
|
||||
(ulong)vertexCount,
|
||||
(ulong)instanceCount,
|
||||
(ulong)firstInstance);
|
||||
|
||||
if (debugGroupName != String.Empty)
|
||||
{
|
||||
PopDebugGroup();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private IndexBufferPattern GetIndexBufferPattern()
|
||||
{
|
||||
return _encoderStateManager.Topology switch
|
||||
{
|
||||
PrimitiveTopology.Quads => QuadsToTrisPattern,
|
||||
PrimitiveTopology.TriangleFan or PrimitiveTopology.Polygon => TriFanToTrisPattern,
|
||||
_ => throw new NotSupportedException($"Unsupported topology: {_encoderStateManager.Topology}"),
|
||||
};
|
||||
}
|
||||
|
||||
private PrimitiveTopology TopologyRemap(PrimitiveTopology topology)
|
||||
{
|
||||
return topology switch
|
||||
{
|
||||
PrimitiveTopology.Quads => PrimitiveTopology.Triangles,
|
||||
PrimitiveTopology.QuadStrip => PrimitiveTopology.TriangleStrip,
|
||||
PrimitiveTopology.TriangleFan or PrimitiveTopology.Polygon => PrimitiveTopology.Triangles,
|
||||
_ => topology,
|
||||
};
|
||||
}
|
||||
|
||||
private bool TopologyUnsupported(PrimitiveTopology topology)
|
||||
{
|
||||
return topology switch
|
||||
{
|
||||
PrimitiveTopology.Quads or PrimitiveTopology.TriangleFan or PrimitiveTopology.Polygon => true,
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
||||
public void DrawIndexed(int indexCount, int instanceCount, int firstIndex, int firstVertex, int firstInstance)
|
||||
{
|
||||
if (indexCount == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
MTLBuffer mtlBuffer;
|
||||
int offset;
|
||||
MTLIndexType type;
|
||||
int finalIndexCount = indexCount;
|
||||
|
||||
MTLPrimitiveType primitiveType = TopologyRemap(_encoderStateManager.Topology).Convert();
|
||||
|
||||
if (TopologyUnsupported(_encoderStateManager.Topology))
|
||||
{
|
||||
IndexBufferPattern pattern = GetIndexBufferPattern();
|
||||
int convertedCount = pattern.GetConvertedCount(indexCount);
|
||||
|
||||
finalIndexCount = convertedCount;
|
||||
|
||||
(mtlBuffer, offset, type) = _encoderStateManager.IndexBuffer.GetConvertedIndexBuffer(_renderer, Cbs, firstIndex, indexCount, convertedCount, pattern);
|
||||
}
|
||||
else
|
||||
{
|
||||
(mtlBuffer, offset, type) = _encoderStateManager.IndexBuffer.GetIndexBuffer(_renderer, Cbs);
|
||||
}
|
||||
|
||||
if (mtlBuffer.NativePtr != IntPtr.Zero)
|
||||
{
|
||||
MTLRenderCommandEncoder renderCommandEncoder = GetOrCreateRenderEncoder(true);
|
||||
|
||||
renderCommandEncoder.DrawIndexedPrimitives(
|
||||
primitiveType,
|
||||
(ulong)finalIndexCount,
|
||||
type,
|
||||
mtlBuffer,
|
||||
(ulong)offset,
|
||||
(ulong)instanceCount,
|
||||
firstVertex,
|
||||
(ulong)firstInstance);
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawIndexedIndirect(BufferRange indirectBuffer)
|
||||
{
|
||||
DrawIndexedIndirectOffset(indirectBuffer);
|
||||
}
|
||||
|
||||
public void DrawIndexedIndirectOffset(BufferRange indirectBuffer, int offset = 0)
|
||||
{
|
||||
// TODO: Reindex unsupported topologies
|
||||
if (TopologyUnsupported(_encoderStateManager.Topology))
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Gpu, $"Drawing indexed with unsupported topology: {_encoderStateManager.Topology}");
|
||||
}
|
||||
|
||||
MTLBuffer buffer = _renderer.BufferManager
|
||||
.GetBuffer(indirectBuffer.Handle, indirectBuffer.Offset, indirectBuffer.Size, false)
|
||||
.Get(Cbs, indirectBuffer.Offset, indirectBuffer.Size).Value;
|
||||
|
||||
MTLPrimitiveType primitiveType = TopologyRemap(_encoderStateManager.Topology).Convert();
|
||||
|
||||
(MTLBuffer indexBuffer, int indexOffset, MTLIndexType type) = _encoderStateManager.IndexBuffer.GetIndexBuffer(_renderer, Cbs);
|
||||
|
||||
if (indexBuffer.NativePtr != IntPtr.Zero && buffer.NativePtr != IntPtr.Zero)
|
||||
{
|
||||
MTLRenderCommandEncoder renderCommandEncoder = GetOrCreateRenderEncoder(true);
|
||||
|
||||
renderCommandEncoder.DrawIndexedPrimitives(
|
||||
primitiveType,
|
||||
type,
|
||||
indexBuffer,
|
||||
(ulong)indexOffset,
|
||||
buffer,
|
||||
(ulong)(indirectBuffer.Offset + offset));
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawIndexedIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride)
|
||||
{
|
||||
for (int i = 0; i < maxDrawCount; i++)
|
||||
{
|
||||
DrawIndexedIndirectOffset(indirectBuffer, stride * i);
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawIndirect(BufferRange indirectBuffer)
|
||||
{
|
||||
DrawIndirectOffset(indirectBuffer);
|
||||
}
|
||||
|
||||
public void DrawIndirectOffset(BufferRange indirectBuffer, int offset = 0)
|
||||
{
|
||||
if (TopologyUnsupported(_encoderStateManager.Topology))
|
||||
{
|
||||
// TODO: Reindex unsupported topologies
|
||||
Logger.Warning?.Print(LogClass.Gpu, $"Drawing indirect with unsupported topology: {_encoderStateManager.Topology}");
|
||||
}
|
||||
|
||||
MTLBuffer buffer = _renderer.BufferManager
|
||||
.GetBuffer(indirectBuffer.Handle, indirectBuffer.Offset, indirectBuffer.Size, false)
|
||||
.Get(Cbs, indirectBuffer.Offset, indirectBuffer.Size).Value;
|
||||
|
||||
MTLPrimitiveType primitiveType = TopologyRemap(_encoderStateManager.Topology).Convert();
|
||||
MTLRenderCommandEncoder renderCommandEncoder = GetOrCreateRenderEncoder(true);
|
||||
|
||||
renderCommandEncoder.DrawPrimitives(
|
||||
primitiveType,
|
||||
buffer,
|
||||
(ulong)(indirectBuffer.Offset + offset));
|
||||
}
|
||||
|
||||
public void DrawIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride)
|
||||
{
|
||||
for (int i = 0; i < maxDrawCount; i++)
|
||||
{
|
||||
DrawIndirectOffset(indirectBuffer, stride * i);
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawTexture(ITexture texture, ISampler sampler, Extents2DF srcRegion, Extents2DF dstRegion)
|
||||
{
|
||||
_renderer.HelperShader.DrawTexture(texture, sampler, srcRegion, dstRegion);
|
||||
}
|
||||
|
||||
public void SetAlphaTest(bool enable, float reference, CompareOp op)
|
||||
{
|
||||
// This is currently handled using shader specialization, as Metal does not support alpha test.
|
||||
// In the future, we may want to use this to write the reference value into the support buffer,
|
||||
// to avoid creating one version of the shader per reference value used.
|
||||
}
|
||||
|
||||
public void SetBlendState(AdvancedBlendDescriptor blend)
|
||||
{
|
||||
// Metal does not support advanced blend.
|
||||
}
|
||||
|
||||
public void SetBlendState(int index, BlendDescriptor blend)
|
||||
{
|
||||
_encoderStateManager.UpdateBlendDescriptors(index, blend);
|
||||
}
|
||||
|
||||
public void SetDepthBias(PolygonModeMask enables, float factor, float units, float clamp)
|
||||
{
|
||||
if (enables == 0)
|
||||
{
|
||||
_encoderStateManager.UpdateDepthBias(0, 0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
_encoderStateManager.UpdateDepthBias(units, factor, clamp);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetDepthClamp(bool clamp)
|
||||
{
|
||||
_encoderStateManager.UpdateDepthClamp(clamp);
|
||||
}
|
||||
|
||||
public void SetDepthMode(DepthMode mode)
|
||||
{
|
||||
// Metal does not support depth clip control.
|
||||
}
|
||||
|
||||
public void SetDepthTest(DepthTestDescriptor depthTest)
|
||||
{
|
||||
_encoderStateManager.UpdateDepthState(depthTest);
|
||||
}
|
||||
|
||||
public void SetFaceCulling(bool enable, Face face)
|
||||
{
|
||||
_encoderStateManager.UpdateCullMode(enable, face);
|
||||
}
|
||||
|
||||
public void SetFrontFace(FrontFace frontFace)
|
||||
{
|
||||
_encoderStateManager.UpdateFrontFace(frontFace);
|
||||
}
|
||||
|
||||
public void SetIndexBuffer(BufferRange buffer, IndexType type)
|
||||
{
|
||||
_encoderStateManager.UpdateIndexBuffer(buffer, type);
|
||||
}
|
||||
|
||||
public void SetImage(ShaderStage stage, int binding, ITexture image)
|
||||
{
|
||||
if (image is TextureBase img)
|
||||
{
|
||||
_encoderStateManager.UpdateImage(stage, binding, img);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetImageArray(ShaderStage stage, int binding, IImageArray array)
|
||||
{
|
||||
if (array is ImageArray imageArray)
|
||||
{
|
||||
_encoderStateManager.UpdateImageArray(stage, binding, imageArray);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetImageArraySeparate(ShaderStage stage, int setIndex, IImageArray array)
|
||||
{
|
||||
if (array is ImageArray imageArray)
|
||||
{
|
||||
_encoderStateManager.UpdateImageArraySeparate(stage, setIndex, imageArray);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetLineParameters(float width, bool smooth)
|
||||
{
|
||||
// Metal does not support wide-lines.
|
||||
}
|
||||
|
||||
public void SetLogicOpState(bool enable, LogicalOp op)
|
||||
{
|
||||
_encoderStateManager.UpdateLogicOpState(enable, op);
|
||||
}
|
||||
|
||||
public void SetMultisampleState(MultisampleDescriptor multisample)
|
||||
{
|
||||
_encoderStateManager.UpdateMultisampleState(multisample);
|
||||
}
|
||||
|
||||
public void SetPatchParameters(int vertices, ReadOnlySpan<float> defaultOuterLevel, ReadOnlySpan<float> defaultInnerLevel)
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!");
|
||||
}
|
||||
|
||||
public void SetPointParameters(float size, bool isProgramPointSize, bool enablePointSprite, Origin origin)
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!");
|
||||
}
|
||||
|
||||
public void SetPolygonMode(PolygonMode frontMode, PolygonMode backMode)
|
||||
{
|
||||
// Metal does not support polygon mode.
|
||||
}
|
||||
|
||||
public void SetPrimitiveRestart(bool enable, int index)
|
||||
{
|
||||
// Always active for LineStrip and TriangleStrip
|
||||
// https://github.com/gpuweb/gpuweb/issues/1220#issuecomment-732483263
|
||||
// https://developer.apple.com/documentation/metal/mtlrendercommandencoder/1515520-drawindexedprimitives
|
||||
// https://stackoverflow.com/questions/70813665/how-to-render-multiple-trianglestrips-using-metal
|
||||
|
||||
// Emulating disabling this is very difficult. It's unlikely for an index buffer to use the largest possible index,
|
||||
// so it's fine nearly all of the time.
|
||||
}
|
||||
|
||||
public void SetPrimitiveTopology(PrimitiveTopology topology)
|
||||
{
|
||||
_encoderStateManager.UpdatePrimitiveTopology(topology);
|
||||
}
|
||||
|
||||
public void SetProgram(IProgram program)
|
||||
{
|
||||
_encoderStateManager.UpdateProgram(program);
|
||||
}
|
||||
|
||||
public void SetRasterizerDiscard(bool discard)
|
||||
{
|
||||
_encoderStateManager.UpdateRasterizerDiscard(discard);
|
||||
}
|
||||
|
||||
public void SetRenderTargetColorMasks(ReadOnlySpan<uint> componentMask)
|
||||
{
|
||||
_encoderStateManager.UpdateRenderTargetColorMasks(componentMask);
|
||||
}
|
||||
|
||||
public void SetRenderTargets(ITexture[] colors, ITexture depthStencil)
|
||||
{
|
||||
_encoderStateManager.UpdateRenderTargets(colors, depthStencil);
|
||||
}
|
||||
|
||||
public void SetScissors(ReadOnlySpan<Rectangle<int>> regions)
|
||||
{
|
||||
_encoderStateManager.UpdateScissors(regions);
|
||||
}
|
||||
|
||||
public void SetStencilTest(StencilTestDescriptor stencilTest)
|
||||
{
|
||||
_encoderStateManager.UpdateStencilState(stencilTest);
|
||||
}
|
||||
|
||||
public void SetUniformBuffers(ReadOnlySpan<BufferAssignment> buffers)
|
||||
{
|
||||
_encoderStateManager.UpdateUniformBuffers(buffers);
|
||||
}
|
||||
|
||||
public void SetStorageBuffers(ReadOnlySpan<BufferAssignment> buffers)
|
||||
{
|
||||
_encoderStateManager.UpdateStorageBuffers(buffers);
|
||||
}
|
||||
|
||||
internal void SetStorageBuffers(int first, ReadOnlySpan<Auto<DisposableBuffer>> buffers)
|
||||
{
|
||||
_encoderStateManager.UpdateStorageBuffers(first, buffers);
|
||||
}
|
||||
|
||||
public void SetTextureAndSampler(ShaderStage stage, int binding, ITexture texture, ISampler sampler)
|
||||
{
|
||||
if (texture is TextureBase tex)
|
||||
{
|
||||
if (sampler == null || sampler is SamplerHolder)
|
||||
{
|
||||
_encoderStateManager.UpdateTextureAndSampler(stage, binding, tex, (SamplerHolder)sampler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetTextureArray(ShaderStage stage, int binding, ITextureArray array)
|
||||
{
|
||||
if (array is TextureArray textureArray)
|
||||
{
|
||||
_encoderStateManager.UpdateTextureArray(stage, binding, textureArray);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetTextureArraySeparate(ShaderStage stage, int setIndex, ITextureArray array)
|
||||
{
|
||||
if (array is TextureArray textureArray)
|
||||
{
|
||||
_encoderStateManager.UpdateTextureArraySeparate(stage, setIndex, textureArray);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetUserClipDistance(int index, bool enableClip)
|
||||
{
|
||||
// TODO. Same as Vulkan
|
||||
}
|
||||
|
||||
public void SetVertexAttribs(ReadOnlySpan<VertexAttribDescriptor> vertexAttribs)
|
||||
{
|
||||
_encoderStateManager.UpdateVertexAttribs(vertexAttribs);
|
||||
}
|
||||
|
||||
public void SetVertexBuffers(ReadOnlySpan<VertexBufferDescriptor> vertexBuffers)
|
||||
{
|
||||
_encoderStateManager.UpdateVertexBuffers(vertexBuffers);
|
||||
}
|
||||
|
||||
public void SetViewports(ReadOnlySpan<Viewport> viewports)
|
||||
{
|
||||
_encoderStateManager.UpdateViewports(viewports);
|
||||
}
|
||||
|
||||
public void TextureBarrier()
|
||||
{
|
||||
if (CurrentEncoderType == EncoderType.Render)
|
||||
{
|
||||
Encoders.RenderEncoder.MemoryBarrier(MTLBarrierScope.Textures, MTLRenderStages.RenderStageFragment, MTLRenderStages.RenderStageFragment);
|
||||
}
|
||||
}
|
||||
|
||||
public void TextureBarrierTiled()
|
||||
{
|
||||
TextureBarrier();
|
||||
}
|
||||
|
||||
public bool TryHostConditionalRendering(ICounterEvent value, ulong compare, bool isEqual)
|
||||
{
|
||||
// TODO: Implementable via indirect draw commands
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TryHostConditionalRendering(ICounterEvent value, ICounterEvent compare, bool isEqual)
|
||||
{
|
||||
// TODO: Implementable via indirect draw commands
|
||||
return false;
|
||||
}
|
||||
|
||||
public void EndHostConditionalRendering()
|
||||
{
|
||||
// TODO: Implementable via indirect draw commands
|
||||
}
|
||||
|
||||
public void BeginTransformFeedback(PrimitiveTopology topology)
|
||||
{
|
||||
// Metal does not support transform feedback.
|
||||
}
|
||||
|
||||
public void EndTransformFeedback()
|
||||
{
|
||||
// Metal does not support transform feedback.
|
||||
}
|
||||
|
||||
public void SetTransformFeedbackBuffers(ReadOnlySpan<BufferRange> buffers)
|
||||
{
|
||||
// Metal does not support transform feedback.
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
EndCurrentPass();
|
||||
_encoderStateManager.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,286 +0,0 @@
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.Shader;
|
||||
using SharpMetal.Foundation;
|
||||
using SharpMetal.Metal;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Versioning;
|
||||
|
||||
namespace Ryujinx.Graphics.Metal
|
||||
{
|
||||
[SupportedOSPlatform("macos")]
|
||||
class Program : IProgram
|
||||
{
|
||||
private ProgramLinkStatus _status;
|
||||
private readonly ShaderSource[] _shaders;
|
||||
private readonly GCHandle[] _handles;
|
||||
private int _successCount;
|
||||
|
||||
private readonly MetalRenderer _renderer;
|
||||
|
||||
public MTLFunction VertexFunction;
|
||||
public MTLFunction FragmentFunction;
|
||||
public MTLFunction ComputeFunction;
|
||||
public ComputeSize ComputeLocalSize { get; }
|
||||
|
||||
private HashTableSlim<PipelineUid, MTLRenderPipelineState> _graphicsPipelineCache;
|
||||
private MTLComputePipelineState? _computePipelineCache;
|
||||
private bool _firstBackgroundUse;
|
||||
|
||||
public ResourceBindingSegment[][] BindingSegments { get; }
|
||||
// Argument buffer sizes for Vertex or Compute stages
|
||||
public int[] ArgumentBufferSizes { get; }
|
||||
// Argument buffer sizes for Fragment stage
|
||||
public int[] FragArgumentBufferSizes { get; }
|
||||
|
||||
public Program(
|
||||
MetalRenderer renderer,
|
||||
MTLDevice device,
|
||||
ShaderSource[] shaders,
|
||||
ResourceLayout resourceLayout,
|
||||
ComputeSize computeLocalSize = default)
|
||||
{
|
||||
_renderer = renderer;
|
||||
renderer.Programs.Add(this);
|
||||
|
||||
ComputeLocalSize = computeLocalSize;
|
||||
_shaders = shaders;
|
||||
_handles = new GCHandle[_shaders.Length];
|
||||
|
||||
_status = ProgramLinkStatus.Incomplete;
|
||||
|
||||
for (int i = 0; i < _shaders.Length; i++)
|
||||
{
|
||||
ShaderSource shader = _shaders[i];
|
||||
|
||||
using MTLCompileOptions compileOptions = new()
|
||||
{
|
||||
PreserveInvariance = true,
|
||||
LanguageVersion = MTLLanguageVersion.Version31,
|
||||
};
|
||||
int index = i;
|
||||
|
||||
_handles[i] = device.NewLibrary(StringHelper.NSString(shader.Code), compileOptions, (library, error) => CompilationResultHandler(library, error, index));
|
||||
}
|
||||
|
||||
(BindingSegments, ArgumentBufferSizes, FragArgumentBufferSizes) = BuildBindingSegments(resourceLayout.SetUsages);
|
||||
}
|
||||
|
||||
public void CompilationResultHandler(MTLLibrary library, NSError error, int index)
|
||||
{
|
||||
ShaderSource shader = _shaders[index];
|
||||
|
||||
if (_handles[index].IsAllocated)
|
||||
{
|
||||
_handles[index].Free();
|
||||
}
|
||||
|
||||
if (error != IntPtr.Zero)
|
||||
{
|
||||
Logger.Warning?.PrintMsg(LogClass.Gpu, shader.Code);
|
||||
Logger.Warning?.Print(LogClass.Gpu, $"{shader.Stage} shader linking failed: \n{StringHelper.String(error.LocalizedDescription)}");
|
||||
_status = ProgramLinkStatus.Failure;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (shader.Stage)
|
||||
{
|
||||
case ShaderStage.Compute:
|
||||
ComputeFunction = library.NewFunction(StringHelper.NSString("kernelMain"));
|
||||
break;
|
||||
case ShaderStage.Vertex:
|
||||
VertexFunction = library.NewFunction(StringHelper.NSString("vertexMain"));
|
||||
break;
|
||||
case ShaderStage.Fragment:
|
||||
FragmentFunction = library.NewFunction(StringHelper.NSString("fragmentMain"));
|
||||
break;
|
||||
default:
|
||||
Logger.Warning?.Print(LogClass.Gpu, $"Cannot handle stage {shader.Stage}!");
|
||||
break;
|
||||
}
|
||||
|
||||
_successCount++;
|
||||
|
||||
if (_successCount >= _shaders.Length && _status != ProgramLinkStatus.Failure)
|
||||
{
|
||||
_status = ProgramLinkStatus.Success;
|
||||
}
|
||||
}
|
||||
|
||||
private static (ResourceBindingSegment[][], int[], int[]) BuildBindingSegments(ReadOnlyCollection<ResourceUsageCollection> setUsages)
|
||||
{
|
||||
ResourceBindingSegment[][] segments = new ResourceBindingSegment[setUsages.Count][];
|
||||
int[] argBufferSizes = new int[setUsages.Count];
|
||||
int[] fragArgBufferSizes = new int[setUsages.Count];
|
||||
|
||||
for (int setIndex = 0; setIndex < setUsages.Count; setIndex++)
|
||||
{
|
||||
List<ResourceBindingSegment> currentSegments = [];
|
||||
|
||||
ResourceUsage currentUsage = default;
|
||||
int currentCount = 0;
|
||||
|
||||
for (int index = 0; index < setUsages[setIndex].Usages.Count; index++)
|
||||
{
|
||||
ResourceUsage usage = setUsages[setIndex].Usages[index];
|
||||
|
||||
if (currentUsage.Binding + currentCount != usage.Binding ||
|
||||
currentUsage.Type != usage.Type ||
|
||||
currentUsage.Stages != usage.Stages ||
|
||||
currentUsage.ArrayLength > 1 ||
|
||||
usage.ArrayLength > 1)
|
||||
{
|
||||
if (currentCount != 0)
|
||||
{
|
||||
currentSegments.Add(new ResourceBindingSegment(
|
||||
currentUsage.Binding,
|
||||
currentCount,
|
||||
currentUsage.Type,
|
||||
currentUsage.Stages,
|
||||
currentUsage.ArrayLength > 1));
|
||||
|
||||
int size = currentCount * ResourcePointerSize(currentUsage.Type);
|
||||
if (currentUsage.Stages.HasFlag(ResourceStages.Fragment))
|
||||
{
|
||||
fragArgBufferSizes[setIndex] += size;
|
||||
}
|
||||
|
||||
if (currentUsage.Stages.HasFlag(ResourceStages.Vertex) ||
|
||||
currentUsage.Stages.HasFlag(ResourceStages.Compute))
|
||||
{
|
||||
argBufferSizes[setIndex] += size;
|
||||
}
|
||||
}
|
||||
|
||||
currentUsage = usage;
|
||||
currentCount = usage.ArrayLength;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentCount != 0)
|
||||
{
|
||||
currentSegments.Add(new ResourceBindingSegment(
|
||||
currentUsage.Binding,
|
||||
currentCount,
|
||||
currentUsage.Type,
|
||||
currentUsage.Stages,
|
||||
currentUsage.ArrayLength > 1));
|
||||
|
||||
int size = currentCount * ResourcePointerSize(currentUsage.Type);
|
||||
if (currentUsage.Stages.HasFlag(ResourceStages.Fragment))
|
||||
{
|
||||
fragArgBufferSizes[setIndex] += size;
|
||||
}
|
||||
|
||||
if (currentUsage.Stages.HasFlag(ResourceStages.Vertex) ||
|
||||
currentUsage.Stages.HasFlag(ResourceStages.Compute))
|
||||
{
|
||||
argBufferSizes[setIndex] += size;
|
||||
}
|
||||
}
|
||||
|
||||
segments[setIndex] = currentSegments.ToArray();
|
||||
}
|
||||
|
||||
return (segments, argBufferSizes, fragArgBufferSizes);
|
||||
}
|
||||
|
||||
private static int ResourcePointerSize(ResourceType type)
|
||||
{
|
||||
return (type == ResourceType.TextureAndSampler ? 2 : 1);
|
||||
}
|
||||
|
||||
public ProgramLinkStatus CheckProgramLink(bool blocking)
|
||||
{
|
||||
if (blocking)
|
||||
{
|
||||
while (_status == ProgramLinkStatus.Incomplete)
|
||||
{ }
|
||||
|
||||
return _status;
|
||||
}
|
||||
|
||||
return _status;
|
||||
}
|
||||
|
||||
public byte[] GetBinary()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public void AddGraphicsPipeline(ref PipelineUid key, MTLRenderPipelineState pipeline)
|
||||
{
|
||||
(_graphicsPipelineCache ??= new()).Add(ref key, pipeline);
|
||||
}
|
||||
|
||||
public void AddComputePipeline(MTLComputePipelineState pipeline)
|
||||
{
|
||||
_computePipelineCache = pipeline;
|
||||
}
|
||||
|
||||
public bool TryGetGraphicsPipeline(ref PipelineUid key, out MTLRenderPipelineState pipeline)
|
||||
{
|
||||
if (_graphicsPipelineCache == null)
|
||||
{
|
||||
pipeline = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!_graphicsPipelineCache.TryGetValue(ref key, out pipeline))
|
||||
{
|
||||
if (_firstBackgroundUse)
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Gpu, "Background pipeline compile missed on draw - incorrect pipeline state?");
|
||||
_firstBackgroundUse = false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
_firstBackgroundUse = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryGetComputePipeline(out MTLComputePipelineState pipeline)
|
||||
{
|
||||
if (_computePipelineCache.HasValue)
|
||||
{
|
||||
pipeline = _computePipelineCache.Value;
|
||||
return true;
|
||||
}
|
||||
|
||||
pipeline = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (!_renderer.Programs.Remove(this))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_graphicsPipelineCache != null)
|
||||
{
|
||||
foreach (MTLRenderPipelineState pipeline in _graphicsPipelineCache.Values)
|
||||
{
|
||||
pipeline.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
_computePipelineCache?.Dispose();
|
||||
|
||||
VertexFunction.Dispose();
|
||||
FragmentFunction.Dispose();
|
||||
ComputeFunction.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
using Ryujinx.Graphics.GAL;
|
||||
|
||||
namespace Ryujinx.Graphics.Metal
|
||||
{
|
||||
readonly struct ResourceBindingSegment
|
||||
{
|
||||
public readonly int Binding;
|
||||
public readonly int Count;
|
||||
public readonly ResourceType Type;
|
||||
public readonly ResourceStages Stages;
|
||||
public readonly bool IsArray;
|
||||
|
||||
public ResourceBindingSegment(int binding, int count, ResourceType type, ResourceStages stages, bool isArray)
|
||||
{
|
||||
Binding = binding;
|
||||
Count = count;
|
||||
Type = type;
|
||||
Stages = stages;
|
||||
IsArray = isArray;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.Versioning;
|
||||
|
||||
namespace Ryujinx.Graphics.Metal
|
||||
{
|
||||
[SupportedOSPlatform("macos")]
|
||||
class ResourceLayoutBuilder
|
||||
{
|
||||
private const int TotalSets = MetalRenderer.TotalSets;
|
||||
|
||||
private readonly List<ResourceDescriptor>[] _resourceDescriptors;
|
||||
private readonly List<ResourceUsage>[] _resourceUsages;
|
||||
|
||||
public ResourceLayoutBuilder()
|
||||
{
|
||||
_resourceDescriptors = new List<ResourceDescriptor>[TotalSets];
|
||||
_resourceUsages = new List<ResourceUsage>[TotalSets];
|
||||
|
||||
for (int index = 0; index < TotalSets; index++)
|
||||
{
|
||||
_resourceDescriptors[index] = [];
|
||||
_resourceUsages[index] = [];
|
||||
}
|
||||
}
|
||||
|
||||
public ResourceLayoutBuilder Add(ResourceStages stages, ResourceType type, int binding, bool write = false)
|
||||
{
|
||||
uint setIndex = type switch
|
||||
{
|
||||
ResourceType.UniformBuffer => Constants.ConstantBuffersSetIndex,
|
||||
ResourceType.StorageBuffer => Constants.StorageBuffersSetIndex,
|
||||
ResourceType.TextureAndSampler or ResourceType.BufferTexture => Constants.TexturesSetIndex,
|
||||
ResourceType.Image or ResourceType.BufferImage => Constants.ImagesSetIndex,
|
||||
_ => throw new ArgumentException($"Invalid resource type \"{type}\"."),
|
||||
};
|
||||
|
||||
_resourceDescriptors[setIndex].Add(new ResourceDescriptor(binding, 1, type, stages));
|
||||
_resourceUsages[setIndex].Add(new ResourceUsage(binding, 1, type, stages, write));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public ResourceLayout Build()
|
||||
{
|
||||
ResourceDescriptorCollection[] descriptors = new ResourceDescriptorCollection[TotalSets];
|
||||
ResourceUsageCollection[] usages = new ResourceUsageCollection[TotalSets];
|
||||
|
||||
for (int index = 0; index < TotalSets; index++)
|
||||
{
|
||||
descriptors[index] = new ResourceDescriptorCollection(_resourceDescriptors[index].ToArray().AsReadOnly());
|
||||
usages[index] = new ResourceUsageCollection(_resourceUsages[index].ToArray().AsReadOnly());
|
||||
}
|
||||
|
||||
return new ResourceLayout(descriptors.AsReadOnly(), usages.AsReadOnly());
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user