mirror of
https://git.ryujinx.app/projects/Kenji-NX.git
synced 2026-09-27 18:48:31 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c3bfce3378 | ||
|
|
88e8d1309c | ||
|
|
ebeb532099 | ||
|
|
c501fe264b | ||
|
|
61d0430006 | ||
|
|
cd477cf744 | ||
|
|
0487b781dc | ||
|
|
51b32981b6 | ||
|
|
fa682d406e | ||
|
|
503dea74c2 | ||
|
|
1f979e5c6f | ||
|
|
378dc33cca | ||
|
|
9c37a557dd | ||
|
|
6e5bd0c9f2 | ||
|
|
0f2b11d4d5 | ||
|
|
1abf5b82c5 | ||
|
|
d751cfa81a | ||
|
|
af14fadb2f | ||
|
|
4fd4e3a4ff | ||
|
|
a1834900be | ||
|
|
fe7a30c747 | ||
|
|
3a387309b4 | ||
|
|
867a92a8bc | ||
|
|
45bf443c01 | ||
|
|
a09568d1b7 | ||
|
|
dae7ae7eaf | ||
|
|
c2f54d0a5c | ||
|
|
f4dfdd35b2 | ||
|
|
c56d04a9a9 | ||
|
|
5c6175cebf | ||
|
|
9addccb50f | ||
|
|
17946c78e6 | ||
|
|
29647b6351 | ||
|
|
6e6983799e | ||
|
|
61da23cb9e | ||
|
|
a62deb8cfd | ||
|
|
8ea79ae5d5 | ||
|
|
09f3bf4b55 | ||
|
|
171624e7f0 | ||
|
|
01cb33f658 | ||
|
|
121bb5dc76 | ||
|
|
d73ef0e3fb | ||
|
|
46b2da2435 | ||
|
|
aca897c70c | ||
|
|
e7d423cd07 | ||
|
|
48c809a80d | ||
|
|
7fd01214cf | ||
|
|
ef72e9b790 | ||
|
|
d5c9bc662c | ||
|
|
3cf90e808d | ||
|
|
326f744093 | ||
|
|
6cecbaa625 | ||
|
|
1443e1a2ff | ||
|
|
12d7f95468 | ||
|
|
b59a833467 | ||
|
|
50a2caf799 | ||
|
|
7d1fd99d3d | ||
|
|
c16559f20e | ||
|
|
6ecd6111d5 | ||
|
|
0ee71fa064 | ||
|
|
6e8f7e7fcb | ||
|
|
f9780b33bb | ||
|
|
d2a532f971 | ||
|
|
a90db3464d | ||
|
|
c61d171e52 | ||
|
|
8927a78c4d | ||
|
|
aa836dd924 | ||
|
|
878ea70aa9 | ||
|
|
9f0a35ee9b | ||
|
|
ed0ebdec5b | ||
|
|
b5a1272141 | ||
|
|
6768e1fa71 | ||
|
|
5c2d710fbb | ||
|
|
d3f3eacc41 | ||
|
|
5983cb9e5f | ||
|
|
e09f16838e | ||
|
|
18f302c0b0 | ||
|
|
8f77076117 | ||
|
|
39544e323c | ||
|
|
24932b7e1b | ||
|
|
2aaf34937d | ||
|
|
bce3426d26 | ||
|
|
51ca73cb10 | ||
|
|
cd3221abdc | ||
|
|
19dd23c288 | ||
|
|
20f1cef9b2 | ||
|
|
2f400491d5 | ||
|
|
f6351e3fff | ||
|
|
f15ddcf9c4 | ||
|
|
88d904acde | ||
|
|
fbcf57bef5 | ||
|
|
8bfb670e65 | ||
|
|
e00bece65a | ||
|
|
2a03f7b1d4 | ||
|
|
6c7b7d6fc4 | ||
|
|
c43db7d5f6 | ||
|
|
cc92af12a4 | ||
|
|
df21f6019e | ||
|
|
232dc2653e | ||
|
|
98b4ff331c | ||
|
|
846b5b6e8a | ||
|
|
fc0528876f | ||
|
|
299b4cfe1d | ||
|
|
d640f50203 | ||
|
|
95ac0a7a51 | ||
|
|
0dc506317c | ||
|
|
3e3b7d22e6 | ||
|
|
e02463d779 |
+19
-33
@@ -1,6 +1,6 @@
|
|||||||
# Contribution to Ryujinx
|
# Contribution to Kenji-NX
|
||||||
|
|
||||||
You can contribute to Ryujinx with PRs, testing of PRs and issues. Contributing code and other implementations is greatly appreciated alongside simply filing issues for problems you encounter.
|
You can contribute to Kenji-NX with PRs, testing of PRs and issues. Contributing code and other implementations is greatly appreciated alongside simply filing issues for problems you encounter.
|
||||||
Please read the entire document before continuing as it can potentially save everyone involved a significant amount of time.
|
Please read the entire document before continuing as it can potentially save everyone involved a significant amount of time.
|
||||||
|
|
||||||
# Quick Links
|
# Quick Links
|
||||||
@@ -14,13 +14,13 @@ We always welcome bug reports, feature proposals and overall feedback. Here are
|
|||||||
|
|
||||||
### Finding Existing Issues
|
### Finding Existing Issues
|
||||||
|
|
||||||
Before filing a new issue, please search our [open issues](https://github.com/KeatonTheBot/Ryujinx/issues) to check if it already exists.
|
Before filing a new issue, please search our [open issues](https://git.ryujinx.app/kenji-nx/ryujinx/-/issues) to check if it already exists.
|
||||||
|
|
||||||
If you do find an existing issue, please include your own feedback in the discussion. Do consider upvoting (👍 reaction) the original post, as this helps us prioritize popular issues in our backlog.
|
If you do find an existing issue, please include your own feedback in the discussion. Do consider upvoting (👍 reaction) the original post, as this helps us prioritize popular issues in our backlog.
|
||||||
|
|
||||||
### Writing a Good Feature Request
|
### Writing a Good Feature Request
|
||||||
|
|
||||||
Please review any feature requests already opened to both check it has not already been suggested, and to familiarize yourself with the format. When ready to submit a proposal, please use the [Feature Request issue template](https://github.com/Ryujinx/Ryujinx/issues/new?assignees=&labels=&projects=&template=feature_request.yml&title=%5BFeature+Request%5D).
|
Please review any feature requests already opened to both check it has not already been suggested, and to familiarize yourself with the format. When ready to submit a proposal, please go [here](https://git.ryujinx.app/kenji-nx/ryujinx/-/issues).
|
||||||
|
|
||||||
### Writing a Good Bug Report
|
### Writing a Good Bug Report
|
||||||
|
|
||||||
@@ -34,13 +34,13 @@ Ideally, a bug report should contain the following information:
|
|||||||
* A Ryujinx log file of the run instance where the issue occurred. Log files can be found in `[Executable Folder]/Logs` and are named chronologically.
|
* A Ryujinx log file of the run instance where the issue occurred. Log files can be found in `[Executable Folder]/Logs` and are named chronologically.
|
||||||
* Additional information, e.g. is it a regression from previous versions? Are there any known workarounds?
|
* Additional information, e.g. is it a regression from previous versions? Are there any known workarounds?
|
||||||
|
|
||||||
When ready to submit a bug report, please use the [Bug Report issue template](https://github.com/KeatonTheBot/Ryujinx/issues/new?assignees=&labels=bug&projects=&template=bug_report.yml&title=%5BBug%5D).
|
When ready to submit a bug report, please go [here](https://git.ryujinx.app/kenji-nx/ryujinx/-/issues.
|
||||||
|
|
||||||
## Contributing Changes
|
## Contributing Changes
|
||||||
|
|
||||||
Project maintainers will merge changes that both improve the project and meet our standards for code quality.
|
Project maintainers will merge changes that both improve the project and meet our standards for code quality.
|
||||||
|
|
||||||
The [Pull Request Guide](docs/workflow/pr-guide.md) and [License](https://github.com/KeatonTheBot/Ryujinx/blob/master/LICENSE.txt) docs define additional guidance.
|
The [Pull Request Guide](docs/workflow/pr-guide.md) and [License](LICENSE.txt) docs define additional guidance.
|
||||||
|
|
||||||
### DOs and DON'Ts
|
### DOs and DON'Ts
|
||||||
|
|
||||||
@@ -67,30 +67,25 @@ Please do not:
|
|||||||
We use and recommend the following workflow:
|
We use and recommend the following workflow:
|
||||||
|
|
||||||
1. Create or find an issue for your work.
|
1. Create or find an issue for your work.
|
||||||
- You can skip this step for trivial changes.
|
- You can skip this step for trivial changes.
|
||||||
- Get agreement from the team and the community that your proposed change is a good one if it is of significant size or changes core functionality.
|
- Get agreement from the team and the community that your proposed change is a good one if it is of significant size or changes core functionality.
|
||||||
- Clearly state that you are going to take on implementing it, if that's the case. You can request that the issue be assigned to you. Note: The issue filer and the implementer don't have to be the same person.
|
- Clearly state that you are going to take on implementing it, if that's the case. You can request that the issue be assigned to you. Note: The issue filer and the implementer don't have to be the same person.
|
||||||
2. Create a personal fork of the repository on GitHub (if you don't already have one).
|
2. Create a personal fork of the repository on GitHub (if you don't already have one).
|
||||||
3. In your fork, create a branch off of main (`git checkout -b mybranch`).
|
3. In your fork, create a branch off of main (`git checkout -b mybranch`).
|
||||||
- Branches are useful since they isolate your changes from incoming changes from upstream. They also enable you to create multiple PRs from the same fork.
|
- Branches are useful since they isolate your changes from incoming changes from upstream. They also enable you to create multiple PRs from the same fork.
|
||||||
4. Make and commit your changes to your branch.
|
4. Make and commit your changes to your branch.
|
||||||
- [Build Instructions](https://github.com/KeatonTheBot/Ryujinx#building) explains how to build and test.
|
- [Build Instructions](https://git.ryujinx.app/kenji-nx/ryujinx#building) explains how to build and test.
|
||||||
- Commit messages should be clear statements of action and intent.
|
- Commit messages should be clear statements of action and intent.
|
||||||
6. Build the repository with your changes.
|
6. Build the repository with your changes.
|
||||||
- Make sure that the builds are clean.
|
- Make sure that the builds are clean.
|
||||||
- Make sure that `dotnet format` has been run and any corrections tested and committed.
|
- Make sure that `dotnet format` has been run and any corrections tested and committed.
|
||||||
7. Create a pull request (PR) against the Ryujinx/Ryujinx repository's **main** branch.
|
7. Create a pull request (PR) against the Ryujinx/Ryujinx repository's **main** branch.
|
||||||
- State in the description what issue or improvement your change is addressing.
|
- State in the description what issue or improvement your change is addressing.
|
||||||
- Check if all the Continuous Integration checks are passing. Refer to [Actions](https://github.com/KeatonTheBot/Ryujinx/actions) to check for outstanding errors.
|
|
||||||
8. Wait for feedback or approval of your changes from the core development team
|
8. Wait for feedback or approval of your changes from the core development team
|
||||||
- Details about the pull request [review procedure](docs/workflow/ci/pr-guide.md).
|
- Details about the pull request [review procedure](docs/workflow/ci/pr-guide.md).
|
||||||
9. When the team members have signed off, and all checks are green, your PR will be merged.
|
9. When the team members have signed off, and all checks are green, your PR will be merged.
|
||||||
- The next official build will automatically include your change.
|
- The next official build will automatically include your change.
|
||||||
- You can delete the branch you used for making the change.
|
- You can delete the branch you used for making the change.
|
||||||
|
|
||||||
### Good First Issues
|
|
||||||
|
|
||||||
The team marks the most straightforward issues as [good first issues](https://github.com/KeatonTheBot/Ryujinx/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22). This set of issues is the place to start if you are interested in contributing but new to the codebase.
|
|
||||||
|
|
||||||
### Commit Messages
|
### Commit Messages
|
||||||
|
|
||||||
@@ -111,15 +106,6 @@ Fix #42
|
|||||||
|
|
||||||
Also do your best to factor commits appropriately, not too large with unrelated things in the same commit, and not too small with the same small change applied N times in N different commits.
|
Also do your best to factor commits appropriately, not too large with unrelated things in the same commit, and not too small with the same small change applied N times in N different commits.
|
||||||
|
|
||||||
### PR - CI Process
|
|
||||||
|
|
||||||
The [Ryujinx continuous integration](https://github.com/KeatonTheBot/Ryujinx/actions) (CI) system will automatically perform the required builds and run tests (including the ones you are expected to run) for PRs. Builds and test runs must be clean or have bugs properly filed against flaky/unexpected failures that are unrelated to your change.
|
|
||||||
|
|
||||||
If the CI build fails for any reason, the PR actions tab should be consulted for further information on the failure. There are a few usual suspects for such a failure:
|
|
||||||
* `dotnet format` has not been run on the PR and has outstanding stylistic issues.
|
|
||||||
* There is an error within the PR that fails a test or errors the compiler.
|
|
||||||
* Random failure of the workflow can occasionally result in a CI failure. In this scenario a maintainer will manually restart the job.
|
|
||||||
|
|
||||||
### PR Feedback
|
### PR Feedback
|
||||||
|
|
||||||
Ryujinx team and community members will provide feedback on your change. Community feedback is highly valued. You may see the absence of team feedback if the community has already provided good review feedback.
|
Ryujinx team and community members will provide feedback on your change. Community feedback is highly valued. You may see the absence of team feedback if the community has already provided good review feedback.
|
||||||
@@ -134,5 +120,5 @@ Ryujinx uses some implementations and frameworks from other projects. The follow
|
|||||||
|
|
||||||
- The license of the file is [permissive](https://en.wikipedia.org/wiki/Permissive_free_software_licence).
|
- The license of the file is [permissive](https://en.wikipedia.org/wiki/Permissive_free_software_licence).
|
||||||
- The license of the file is left in-tact.
|
- The license of the file is left in-tact.
|
||||||
- The contribution is correctly attributed in the [3rd party notices](https://github.com/KeatonTheBot/Ryujinx/blob/master/distribution/legal/THIRDPARTY.md) file in the repository, as needed.
|
- The contribution is correctly attributed in the [3rd party notices](distribution/legal/THIRDPARTY.md) file in the repository, as needed.
|
||||||
|
|
||||||
|
|||||||
+25
-26
@@ -3,42 +3,41 @@
|
|||||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageVersion Include="Avalonia" Version="11.0.13" />
|
<PackageVersion Include="Avalonia" Version="11.3.6" />
|
||||||
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.0.13" />
|
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.3.6" />
|
||||||
<PackageVersion Include="Avalonia.Desktop" Version="11.0.13" />
|
<PackageVersion Include="Avalonia.Desktop" Version="11.3.6" />
|
||||||
<PackageVersion Include="Avalonia.Diagnostics" Version="11.0.13" />
|
<PackageVersion Include="Avalonia.Diagnostics" Version="11.3.6" />
|
||||||
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="11.0.13" />
|
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="11.3.6" />
|
||||||
<PackageVersion Include="Avalonia.Svg" Version="11.0.0.19" />
|
<PackageVersion Include="Svg.Controls.Avalonia" Version="11.3.6.2" />
|
||||||
<PackageVersion Include="Avalonia.Svg.Skia" Version="11.0.0.19" />
|
<PackageVersion Include="Svg.Controls.Skia.Avalonia" Version="11.3.6.2" />
|
||||||
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
|
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
|
||||||
<PackageVersion Include="Concentus" Version="2.2.2" />
|
<PackageVersion Include="Concentus" Version="2.2.2" />
|
||||||
<PackageVersion Include="DiscordRichPresence" Version="1.2.1.24" />
|
<PackageVersion Include="DiscordRichPresence" Version="1.6.1.70" />
|
||||||
<PackageVersion Include="DynamicData" Version="9.0.4" />
|
<PackageVersion Include="DynamicData" Version="9.4.1" />
|
||||||
<PackageVersion Include="FluentAvaloniaUI" Version="2.0.5" />
|
<PackageVersion Include="FluentAvaloniaUI.NoAnim" Version="2.4.0-build2" />
|
||||||
<PackageVersion Include="Gommon" Version="2.7.1.1" />
|
<PackageVersion Include="Gommon" Version="2.7.1.1" />
|
||||||
<PackageVersion Include="GtkSharp.Dependencies" Version="1.1.1" />
|
|
||||||
<PackageVersion Include="GtkSharp.Dependencies.osx" Version="0.0.5" />
|
|
||||||
<PackageVersion Include="Humanizer" Version="2.14.1" />
|
<PackageVersion Include="Humanizer" Version="2.14.1" />
|
||||||
<PackageVersion Include="LibHac" Version="0.19.0" />
|
|
||||||
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
|
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
|
||||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2" />
|
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.12.0" />
|
||||||
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.9.0" />
|
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.14.0" />
|
||||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
|
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
|
||||||
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.1" />
|
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.1" />
|
||||||
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
|
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
|
||||||
<PackageVersion Include="NetCoreServer" Version="8.0.7" />
|
<PackageVersion Include="NetCoreServer" Version="8.0.7" />
|
||||||
<PackageVersion Include="NUnit" Version="3.13.3" />
|
<PackageVersion Include="NUnit" Version="3.13.3" />
|
||||||
<PackageVersion Include="NUnit3TestAdapter" Version="4.1.0" />
|
<PackageVersion Include="NUnit3TestAdapter" Version="4.1.0" />
|
||||||
<PackageVersion Include="OpenTK.Core" Version="4.8.2" />
|
<PackageVersion Include="OpenTK.Core" Version="4.9.4" />
|
||||||
<PackageVersion Include="OpenTK.Graphics" Version="4.8.2" />
|
<PackageVersion Include="OpenTK.Graphics" Version="4.9.4" />
|
||||||
<PackageVersion Include="OpenTK.Audio.OpenAL" Version="4.8.2" />
|
<PackageVersion Include="OpenTK.Audio.OpenAL" Version="4.9.4" />
|
||||||
<PackageVersion Include="OpenTK.Windowing.GraphicsLibraryFramework" Version="4.8.2" />
|
<PackageVersion Include="OpenTK.Windowing.GraphicsLibraryFramework" Version="4.9.4" />
|
||||||
<PackageVersion Include="Open.NAT.Core" Version="2.1.0.5" />
|
<PackageVersion Include="Open.NAT.Core" Version="2.1.0.5" />
|
||||||
<PackageVersion Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" />
|
<PackageVersion Include="Ryujinx.Audio.OpenAL" Version="1.24.3" />
|
||||||
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies.AllArch" Version="6.1.2-build3" />
|
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies.Linux" Version="6.1.3-build5" />
|
||||||
|
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies.macOS" Version="5.0.3-build14" />
|
||||||
|
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies.Windows" Version="6.1.3-build5" />
|
||||||
<PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" />
|
<PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" />
|
||||||
<PackageVersion Include="Ryujinx.GtkSharp" Version="3.24.24.59-ryujinx" />
|
<PackageVersion Include="Ryujinx.LibHac" Version="0.21.0-alpha.116" />
|
||||||
<PackageVersion Include="Ryujinx.SDL2-CS" Version="2.30.0-build32" />
|
<PackageVersion Include="Ryujinx.SDL2-CS-Redux" Version="2.32.10" />
|
||||||
<PackageVersion Include="securifybv.ShellLink" Version="0.1.0" />
|
<PackageVersion Include="securifybv.ShellLink" Version="0.1.0" />
|
||||||
<PackageVersion Include="shaderc.net" Version="0.1.0" />
|
<PackageVersion Include="shaderc.net" Version="0.1.0" />
|
||||||
<PackageVersion Include="SharpZipLib" Version="1.4.2" />
|
<PackageVersion Include="SharpZipLib" Version="1.4.2" />
|
||||||
@@ -48,8 +47,8 @@
|
|||||||
<PackageVersion Include="SkiaSharp" Version="2.88.9" />
|
<PackageVersion Include="SkiaSharp" Version="2.88.9" />
|
||||||
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="2.88.9" />
|
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="2.88.9" />
|
||||||
<PackageVersion Include="SPB" Version="0.0.4-build32" />
|
<PackageVersion Include="SPB" Version="0.0.4-build32" />
|
||||||
<PackageVersion Include="System.IO.Hashing" Version="9.0.4" />
|
<PackageVersion Include="System.IO.Hashing" Version="9.0.9" />
|
||||||
<PackageVersion Include="System.Management" Version="9.0.4" />
|
<PackageVersion Include="System.Management" Version="9.0.9" />
|
||||||
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />
|
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.1.3" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
<h1 align="center">
|
<h1 align="center">
|
||||||
<br>
|
<br>
|
||||||
<img src="distribution/misc/Logo.png" alt="Kenji-NX">
|
<img src="https://git.ryujinx.app/kenji-nx/ryujinx/-/raw/master/distribution/misc/Logo.png" alt="Kenji-NX">
|
||||||
<br>
|
<br>
|
||||||
<b>Kenji-NX</b>
|
<b>Kenji-NX</b>
|
||||||
<br>
|
<br>
|
||||||
<a href="https://github.com/KeatonTheBot/Kenji-NX/releases/latest">
|
<a href="https://github.com/Kenji-NX/Releases/releases/latest">
|
||||||
<img src="https://img.shields.io/github/v/release/KeatonTheBot/Kenji-NX"
|
<img src="https://img.shields.io/github/v/release/Kenji-NX/Releases"
|
||||||
alt="Latest Release">
|
alt="Latest Release">
|
||||||
</a>
|
</a>
|
||||||
</h1>
|
</h1>
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
Kenji-NX is an open-source Nintendo Switch emulator, originally created by gdkchan, written in C#.
|
Kenji-NX is an open-source Nintendo Switch emulator, originally created by gdkchan, written in C#.
|
||||||
This emulator aims at providing excellent accuracy and performance, a user-friendly interface and consistent builds.
|
This emulator aims at providing excellent accuracy and performance, a user-friendly interface and consistent builds.
|
||||||
It was written from scratch and development on the project began in September 2017.
|
It was written from scratch and development on the project began in September 2017.
|
||||||
Kenji-NX is available on GitHub under the <a href="https://github.com/KeatonTheBot/Kenji-NX/blob/master/LICENSE.txt" target="_blank">MIT license</a>.
|
Kenji-NX is available on GitHub under the <a href="https://git.ryujinx.app/kenji-nx/ryujinx/-/raw/master/LICENSE.txt" target="_blank">MIT license</a>.
|
||||||
<br><br>
|
<br><br>
|
||||||
On October 1st 2024, Ryujinx was discontinued as the creator was forced to abandon the project.
|
On October 1st 2024, Ryujinx was discontinued as the creator was forced to abandon the project.
|
||||||
<br><br>
|
<br><br>
|
||||||
@@ -63,7 +63,7 @@ Make sure your SDK version is higher or equal to the required version specified
|
|||||||
|
|
||||||
### Step 2
|
### Step 2
|
||||||
|
|
||||||
Either use `git clone https://github.com/KeatonTheBot/Kenji-NX` on the command line to clone the repository or use Code --> Download zip button to get the files.
|
Either use `git clone https://git.ryujinx.app/kenji-nx/ryujinx` on the command line to clone the repository or use Code --> Download zip button to get the files.
|
||||||
|
|
||||||
### Step 3
|
### Step 3
|
||||||
|
|
||||||
|
|||||||
@@ -63,8 +63,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.SDL2.Common", "src\
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio.Backends.SDL2", "src\Ryujinx.Audio.Backends.SDL2\Ryujinx.Audio.Backends.SDL2.csproj", "{D99A395A-8569-4DB0-B336-900647890052}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio.Backends.SDL2", "src\Ryujinx.Audio.Backends.SDL2\Ryujinx.Audio.Backends.SDL2.csproj", "{D99A395A-8569-4DB0-B336-900647890052}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Headless.SDL2", "src\Ryujinx.Headless.SDL2\Ryujinx.Headless.SDL2.csproj", "{390DC343-5CB4-4C79-A5DD-E3ED235E4C49}"
|
|
||||||
EndProject
|
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Nvdec.FFmpeg", "src\Ryujinx.Graphics.Nvdec.FFmpeg\Ryujinx.Graphics.Nvdec.FFmpeg.csproj", "{BEE1C184-C9A4-410B-8DFC-FB74D5C93AEB}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Nvdec.FFmpeg", "src\Ryujinx.Graphics.Nvdec.FFmpeg\Ryujinx.Graphics.Nvdec.FFmpeg.csproj", "{BEE1C184-C9A4-410B-8DFC-FB74D5C93AEB}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx", "src\Ryujinx\Ryujinx.csproj", "{7C1B2721-13DA-4B62-B046-C626605ECCE6}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx", "src\Ryujinx\Ryujinx.csproj", "{7C1B2721-13DA-4B62-B046-C626605ECCE6}"
|
||||||
@@ -201,10 +199,6 @@ Global
|
|||||||
{D99A395A-8569-4DB0-B336-900647890052}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{D99A395A-8569-4DB0-B336-900647890052}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{D99A395A-8569-4DB0-B336-900647890052}.Release|Any CPU.ActiveCfg = Release|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}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{390DC343-5CB4-4C79-A5DD-E3ED235E4C49}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{390DC343-5CB4-4C79-A5DD-E3ED235E4C49}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{390DC343-5CB4-4C79-A5DD-E3ED235E4C49}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{390DC343-5CB4-4C79-A5DD-E3ED235E4C49}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{BEE1C184-C9A4-410B-8DFC-FB74D5C93AEB}.Debug|Any CPU.ActiveCfg = Debug|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}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{BEE1C184-C9A4-410B-8DFC-FB74D5C93AEB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{BEE1C184-C9A4-410B-8DFC-FB74D5C93AEB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
|||||||
+3
-3
@@ -1,11 +1,11 @@
|
|||||||
# Documents Index
|
# Documents Index
|
||||||
|
|
||||||
This repo includes several documents that explain both high-level and low-level concepts about Ryujinx and its functions. These are very useful for contributors, to get context that can be very difficult to acquire from just reading code.
|
This repo includes several documents that explain both high-level and low-level concepts about Kenji-NX and its functions. These are very useful for contributors, to get context that can be very difficult to acquire from just reading code.
|
||||||
|
|
||||||
Intro to Ryujinx
|
Intro to Kenji-NX
|
||||||
==================
|
==================
|
||||||
|
|
||||||
Ryujinx is an open-source Nintendo Switch emulator, created by gdkchan, written in C#.
|
Kenji-NX is an open-source Nintendo Switch emulator written in C#. It is based on Ryujinx, which was originally created by gdkchan.
|
||||||
* The CPU emulator, ARMeilleure, emulates an ARMv8 CPU and currently has support for most 64-bit ARMv8 and some of the ARMv7 (and older) instructions.
|
* The CPU emulator, ARMeilleure, emulates an ARMv8 CPU and currently has support for most 64-bit ARMv8 and some of the ARMv7 (and older) instructions.
|
||||||
* The GPU emulator emulates the Switch's Maxwell GPU using either the OpenGL (version 4.5 minimum), Vulkan, or Metal (via MoltenVK) APIs through a custom build of OpenTK or Silk.NET respectively.
|
* The GPU emulator emulates the Switch's Maxwell GPU using either the OpenGL (version 4.5 minimum), Vulkan, or Metal (via MoltenVK) APIs through a custom build of OpenTK or Silk.NET respectively.
|
||||||
* Audio output is entirely supported via C# wrappers for SDL2, with OpenAL & libsoundio as fallbacks.
|
* Audio output is entirely supported via C# wrappers for SDL2, with OpenAL & libsoundio as fallbacks.
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
## Contributing Rules
|
## Contributing Rules
|
||||||
|
|
||||||
All contributions to KeatonTheBot/Ryujinx repository are made via pull requests (PRs) rather than through direct commits. The pull requests are reviewed and merged by the maintainers after a review and at least two approvals from the core development team.
|
All contributions to Kenji-NX repository are made via pull requests (PRs) rather than through direct commits. The pull requests are reviewed and merged by the maintainers after a review and at least two approvals from the core development team.
|
||||||
|
|
||||||
To merge pull requests, you must have write permissions in the repository.
|
To merge pull requests, you must have write permissions in the repository.
|
||||||
|
|
||||||
@@ -18,17 +18,13 @@ To merge pull requests, you must have write permissions in the repository.
|
|||||||
|
|
||||||
## Pull Request Ownership
|
## Pull Request Ownership
|
||||||
|
|
||||||
Every pull request will have automatically have labels and reviewers assigned. The label not only indicates the code segment which the change touches but also the area reviewers to be assigned.
|
Every pull request will automatically have labels and reviewers assigned. The label not only indicates the code segment which the change touches but also the area reviewers to be assigned.
|
||||||
|
|
||||||
If during the code review process a merge conflict occurs, the PR author is responsible for its resolution. Help will be provided if necessary although GitHub makes this easier by allowing simple conflict resolution using the [conflict-editor](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/resolving-a-merge-conflict-on-github).
|
If during the code review process a merge conflict occurs, the PR author is responsible for its resolution. Help will be provided if necessary although GitHub makes this easier by allowing simple conflict resolution using the [conflict-editor](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/resolving-a-merge-conflict-on-github).
|
||||||
|
|
||||||
## Pull Request Builds
|
|
||||||
|
|
||||||
When submitting a PR to the `KeatonTheBot/Ryujinx` repository, various builds will run validating many areas to ensure we keep developer productivity and product quality high. These various workflows can be tracked in the [Actions](https://github.com/KeatonTheBot/Ryujinx/actions) tab of the repository. If the job continues to completion, the build artifacts will be uploaded and posted as a comment in the PR discussion.
|
|
||||||
|
|
||||||
## Review Turnaround Times
|
## Review Turnaround Times
|
||||||
|
|
||||||
Ryujinx is a project that is maintained by volunteers on a completely free-time basis. As such we cannot guarantee any particular timeframe for pull request review and approval. Weeks to months are common for larger (>500 line) PRs but there are some additional best practises to avoid review purgatory.
|
Kenji-NX is a project that is maintained by volunteers on a completely free-time basis. As such we cannot guarantee any particular timeframe for pull request review and approval. Weeks to months are common for larger (>500 line) PRs but there are some additional best practises to avoid review purgatory.
|
||||||
|
|
||||||
* Make the reviewers life easier wherever possible. Make use of descriptive commit names, code comments and XML docs where applicable.
|
* Make the reviewers life easier wherever possible. Make use of descriptive commit names, code comments and XML docs where applicable.
|
||||||
* If there is disagreement on feedback then always lean on the side of the development team and community over any personal opinion.
|
* If there is disagreement on feedback then always lean on the side of the development team and community over any personal opinion.
|
||||||
@@ -41,8 +37,7 @@ To re-iterate, make the review as easy for us as possible, respond promptly and
|
|||||||
Anyone with write access can merge a pull request manually when the following conditions have been met:
|
Anyone with write access can merge a pull request manually when the following conditions have been met:
|
||||||
|
|
||||||
* The PR has been approved by two reviewers and any other objections are addressed.
|
* The PR has been approved by two reviewers and any other objections are addressed.
|
||||||
* You can request follow up reviews from the original reviewers if they requested changes.
|
* You can request follow-up reviews from the original reviewers if they requested changes.
|
||||||
* The PR successfully builds and passes all tests in the Continuous Integration (CI) system. In case of failures, refer to the [Actions](https://github.com/KeatonTheBot/Ryujinx/actions) tab of your PR.
|
|
||||||
|
|
||||||
Typically, PRs are merged as one commit (squash merges). It creates a simpler history than a Merge Commit. "Special circumstances" are rare, and typically mean that there are a series of cleanly separated changes that will be too hard to understand if squashed together, or for some reason we want to preserve the ability to dissect them.
|
Typically, PRs are merged as one commit (squash merges). It creates a simpler history than a Merge Commit. "Special circumstances" are rare, and typically mean that there are a series of cleanly separated changes that will be too hard to understand if squashed together, or for some reason we want to preserve the ability to dissect them.
|
||||||
|
|
||||||
|
|||||||
+16
-4
@@ -1,7 +1,19 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<configuration>
|
<configuration>
|
||||||
<packageSources>
|
<packageSources>
|
||||||
<clear />
|
<clear />
|
||||||
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
|
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
|
||||||
</packageSources>
|
<!-- Only needed when using pre-release versions of Ryujinx.LibHac. -->
|
||||||
|
<add key="LibHacAlpha" value="https://git.ryujinx.app/api/v4/projects/17/packages/nuget/index.json" />
|
||||||
|
</packageSources>
|
||||||
|
<packageSourceMapping>
|
||||||
|
<!-- key value for <packageSource> should match key values from <packageSources> element -->
|
||||||
|
<!-- These are defined and .NET still yells about multiple package sources with no mappings. Not sure what to do, this is in the docs lol -->
|
||||||
|
<packageSource key="nuget.org">
|
||||||
|
<package pattern="*" />
|
||||||
|
</packageSource>
|
||||||
|
<packageSource key="LibHacAlpha">
|
||||||
|
<package pattern="Ryujinx.LibHac" />
|
||||||
|
</packageSource>
|
||||||
|
</packageSourceMapping>
|
||||||
</configuration>
|
</configuration>
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
|
<RuntimeIdentifiers>osx-arm64</RuntimeIdentifiers>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
|
||||||
<ProjectReference Include="..\Ryujinx.Memory\Ryujinx.Memory.csproj" />
|
<ProjectReference Include="..\Ryujinx.Memory\Ryujinx.Memory.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -254,7 +254,7 @@ namespace ARMeilleure.CodeGen.Arm64
|
|||||||
|
|
||||||
private static bool IsMemoryLoadOrStore(Instruction inst)
|
private static bool IsMemoryLoadOrStore(Instruction inst)
|
||||||
{
|
{
|
||||||
return inst == Instruction.Load || inst == Instruction.Store;
|
return inst is Instruction.Load or Instruction.Store;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool ConstTooLong(Operand constOp, OperandType accessType)
|
private static bool ConstTooLong(Operand constOp, OperandType accessType)
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ namespace ARMeilleure.CodeGen.Arm64
|
|||||||
// Any value AND all ones will be equal itself, so it's effectively a no-op.
|
// Any value AND all ones will be equal itself, so it's effectively a no-op.
|
||||||
// Any value OR all ones will be equal all ones, so one can just use MOV.
|
// Any value OR all ones will be equal all ones, so one can just use MOV.
|
||||||
// Any value XOR all ones will be equal its inverse, so one can just use MVN.
|
// Any value XOR all ones will be equal its inverse, so one can just use MVN.
|
||||||
if (value == 0 || value == ulong.MaxValue)
|
if (value is 0 or ulong.MaxValue)
|
||||||
{
|
{
|
||||||
immN = 0;
|
immN = 0;
|
||||||
immS = 0;
|
immS = 0;
|
||||||
|
|||||||
@@ -189,8 +189,7 @@ namespace ARMeilleure.CodeGen.Arm64
|
|||||||
// The only blocks which can have 0 successors are exit blocks.
|
// The only blocks which can have 0 successors are exit blocks.
|
||||||
Operation last = block.Operations.Last;
|
Operation last = block.Operations.Last;
|
||||||
|
|
||||||
Debug.Assert(last.Instruction == Instruction.Tailcall ||
|
Debug.Assert(last.Instruction is Instruction.Tailcall or Instruction.Return);
|
||||||
last.Instruction == Instruction.Return);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -464,7 +463,7 @@ namespace ARMeilleure.CodeGen.Arm64
|
|||||||
Operand dest = operation.Destination;
|
Operand dest = operation.Destination;
|
||||||
Operand source = operation.GetSource(0);
|
Operand source = operation.GetSource(0);
|
||||||
|
|
||||||
Debug.Assert(dest.Type == OperandType.FP32 || dest.Type == OperandType.FP64);
|
Debug.Assert(dest.Type is OperandType.FP32 or OperandType.FP64);
|
||||||
Debug.Assert(dest.Type != source.Type);
|
Debug.Assert(dest.Type != source.Type);
|
||||||
Debug.Assert(source.Type != OperandType.V128);
|
Debug.Assert(source.Type != OperandType.V128);
|
||||||
|
|
||||||
@@ -483,7 +482,7 @@ namespace ARMeilleure.CodeGen.Arm64
|
|||||||
Operand dest = operation.Destination;
|
Operand dest = operation.Destination;
|
||||||
Operand source = operation.GetSource(0);
|
Operand source = operation.GetSource(0);
|
||||||
|
|
||||||
Debug.Assert(dest.Type == OperandType.FP32 || dest.Type == OperandType.FP64);
|
Debug.Assert(dest.Type is OperandType.FP32 or OperandType.FP64);
|
||||||
Debug.Assert(dest.Type != source.Type);
|
Debug.Assert(dest.Type != source.Type);
|
||||||
Debug.Assert(source.Type.IsInteger());
|
Debug.Assert(source.Type.IsInteger());
|
||||||
|
|
||||||
@@ -1463,7 +1462,7 @@ namespace ARMeilleure.CodeGen.Arm64
|
|||||||
|
|
||||||
private static bool IsLoadOrStore(Operation operation)
|
private static bool IsLoadOrStore(Operation operation)
|
||||||
{
|
{
|
||||||
return operation.Instruction == Instruction.Load || operation.Instruction == Instruction.Store;
|
return operation.Instruction is Instruction.Load or Instruction.Store;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static OperandType GetMemOpValueType(Operation operation)
|
private static OperandType GetMemOpValueType(Operation operation)
|
||||||
@@ -1553,7 +1552,7 @@ namespace ARMeilleure.CodeGen.Arm64
|
|||||||
|
|
||||||
private static void EnsureSameReg(Operand op1, Operand op2)
|
private static void EnsureSameReg(Operand op1, Operand op2)
|
||||||
{
|
{
|
||||||
Debug.Assert(op1.Kind == OperandKind.Register || op1.Kind == OperandKind.Memory);
|
Debug.Assert(op1.Kind is OperandKind.Register or OperandKind.Memory);
|
||||||
Debug.Assert(op1.Kind == op2.Kind);
|
Debug.Assert(op1.Kind == op2.Kind);
|
||||||
Debug.Assert(op1.Value == op2.Value);
|
Debug.Assert(op1.Value == op2.Value);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -736,19 +736,19 @@ namespace ARMeilleure.CodeGen.Arm64
|
|||||||
{
|
{
|
||||||
IntrinsicInfo info = IntrinsicTable.GetInfo(intrinsic & ~(Intrinsic.Arm64VTypeMask | Intrinsic.Arm64VSizeMask));
|
IntrinsicInfo info = IntrinsicTable.GetInfo(intrinsic & ~(Intrinsic.Arm64VTypeMask | Intrinsic.Arm64VSizeMask));
|
||||||
|
|
||||||
return info.Type == IntrinsicType.ScalarBinaryRd ||
|
return info.Type is IntrinsicType.ScalarBinaryRd
|
||||||
info.Type == IntrinsicType.ScalarTernaryFPRdByElem ||
|
or IntrinsicType.ScalarTernaryFPRdByElem
|
||||||
info.Type == IntrinsicType.ScalarTernaryShlRd ||
|
or IntrinsicType.ScalarTernaryShlRd
|
||||||
info.Type == IntrinsicType.ScalarTernaryShrRd ||
|
or IntrinsicType.ScalarTernaryShrRd
|
||||||
info.Type == IntrinsicType.Vector128BinaryRd ||
|
or IntrinsicType.Vector128BinaryRd
|
||||||
info.Type == IntrinsicType.VectorBinaryRd ||
|
or IntrinsicType.VectorBinaryRd
|
||||||
info.Type == IntrinsicType.VectorInsertByElem ||
|
or IntrinsicType.VectorInsertByElem
|
||||||
info.Type == IntrinsicType.VectorTernaryRd ||
|
or IntrinsicType.VectorTernaryRd
|
||||||
info.Type == IntrinsicType.VectorTernaryRdBitwise ||
|
or IntrinsicType.VectorTernaryRdBitwise
|
||||||
info.Type == IntrinsicType.VectorTernaryFPRdByElem ||
|
or IntrinsicType.VectorTernaryFPRdByElem
|
||||||
info.Type == IntrinsicType.VectorTernaryRdByElem ||
|
or IntrinsicType.VectorTernaryRdByElem
|
||||||
info.Type == IntrinsicType.VectorTernaryShlRd ||
|
or IntrinsicType.VectorTernaryShlRd
|
||||||
info.Type == IntrinsicType.VectorTernaryShrRd;
|
or IntrinsicType.VectorTernaryShrRd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool HasConstSrc1(Operation node, ulong value)
|
private static bool HasConstSrc1(Operation node, ulong value)
|
||||||
@@ -849,7 +849,7 @@ namespace ARMeilleure.CodeGen.Arm64
|
|||||||
|
|
||||||
var compType = (Comparison)comp.AsInt32();
|
var compType = (Comparison)comp.AsInt32();
|
||||||
|
|
||||||
return compType == Comparison.Equal || compType == Comparison.NotEqual;
|
return compType is Comparison.Equal or Comparison.NotEqual;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ namespace ARMeilleure.CodeGen.Linking
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets an empty <see cref="RelocInfo"/>.
|
/// Gets an empty <see cref="RelocInfo"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static RelocInfo Empty { get; } = new RelocInfo(null);
|
public static RelocInfo Empty { get; } = new(null);
|
||||||
|
|
||||||
private readonly RelocEntry[] _entries;
|
private readonly RelocEntry[] _entries;
|
||||||
|
|
||||||
|
|||||||
@@ -227,11 +227,11 @@ namespace ARMeilleure.CodeGen.Optimizations
|
|||||||
|
|
||||||
private static bool HasSideEffects(Operation node)
|
private static bool HasSideEffects(Operation node)
|
||||||
{
|
{
|
||||||
return node.Instruction == Instruction.Call
|
return node.Instruction is Instruction.Call
|
||||||
|| node.Instruction == Instruction.Tailcall
|
or Instruction.Tailcall
|
||||||
|| node.Instruction == Instruction.CompareAndSwap
|
or Instruction.CompareAndSwap
|
||||||
|| node.Instruction == Instruction.CompareAndSwap16
|
or Instruction.CompareAndSwap16
|
||||||
|| node.Instruction == Instruction.CompareAndSwap8;
|
or Instruction.CompareAndSwap8;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsPropagableCompare(Operation operation)
|
private static bool IsPropagableCompare(Operation operation)
|
||||||
|
|||||||
@@ -839,7 +839,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
|||||||
{
|
{
|
||||||
dest.NumberLocal(_intervals.Count);
|
dest.NumberLocal(_intervals.Count);
|
||||||
|
|
||||||
LiveInterval interval = new LiveInterval(dest);
|
LiveInterval interval = new(dest);
|
||||||
_intervals.Add(interval);
|
_intervals.Add(interval);
|
||||||
|
|
||||||
SetVisited(dest);
|
SetVisited(dest);
|
||||||
@@ -847,7 +847,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
|||||||
// If this is a copy (or copy-like operation), set the copy source interval as well.
|
// If this is a copy (or copy-like operation), set the copy source interval as well.
|
||||||
// This is used for register preferencing later on, which allows the copy to be eliminated
|
// This is used for register preferencing later on, which allows the copy to be eliminated
|
||||||
// in some cases.
|
// in some cases.
|
||||||
if (node.Instruction == Instruction.Copy || node.Instruction == Instruction.ZeroExtend32)
|
if (node.Instruction is Instruction.Copy or Instruction.ZeroExtend32)
|
||||||
{
|
{
|
||||||
Operand source = node.GetSource(0);
|
Operand source = node.GetSource(0);
|
||||||
|
|
||||||
@@ -1120,8 +1120,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
|||||||
|
|
||||||
private static bool IsLocalOrRegister(OperandKind kind)
|
private static bool IsLocalOrRegister(OperandKind kind)
|
||||||
{
|
{
|
||||||
return kind == OperandKind.LocalVariable ||
|
return kind is OperandKind.LocalVariable or OperandKind.Register;
|
||||||
kind == OperandKind.Register;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1477,7 +1477,7 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
|
|
||||||
private static bool Is64Bits(OperandType type)
|
private static bool Is64Bits(OperandType type)
|
||||||
{
|
{
|
||||||
return type == OperandType.I64 || type == OperandType.FP64;
|
return type is OperandType.I64 or OperandType.FP64;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsImm8(ulong immediate, OperandType type)
|
private static bool IsImm8(ulong immediate, OperandType type)
|
||||||
|
|||||||
@@ -175,8 +175,7 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
// The only blocks which can have 0 successors are exit blocks.
|
// The only blocks which can have 0 successors are exit blocks.
|
||||||
Operation last = block.Operations.Last;
|
Operation last = block.Operations.Last;
|
||||||
|
|
||||||
Debug.Assert(last.Instruction == Instruction.Tailcall ||
|
Debug.Assert(last.Instruction is Instruction.Tailcall or Instruction.Return);
|
||||||
last.Instruction == Instruction.Return);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -478,7 +477,7 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
Debug.Assert(HardwareCapabilities.SupportsVexEncoding);
|
Debug.Assert(HardwareCapabilities.SupportsVexEncoding);
|
||||||
|
|
||||||
Debug.Assert(dest.Kind == OperandKind.Register && src1.Kind == OperandKind.Register && src2.Kind == OperandKind.Register);
|
Debug.Assert(dest.Kind == OperandKind.Register && src1.Kind == OperandKind.Register && src2.Kind == OperandKind.Register);
|
||||||
Debug.Assert(src3.Kind == OperandKind.Register || src3.Kind == OperandKind.Memory);
|
Debug.Assert(src3.Kind is OperandKind.Register or OperandKind.Memory);
|
||||||
|
|
||||||
EnsureSameType(dest, src1, src2, src3);
|
EnsureSameType(dest, src1, src2, src3);
|
||||||
Debug.Assert(dest.Type == OperandType.V128);
|
Debug.Assert(dest.Type == OperandType.V128);
|
||||||
@@ -788,7 +787,7 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
Operand dest = operation.Destination;
|
Operand dest = operation.Destination;
|
||||||
Operand source = operation.GetSource(0);
|
Operand source = operation.GetSource(0);
|
||||||
|
|
||||||
Debug.Assert(dest.Type == OperandType.FP32 || dest.Type == OperandType.FP64);
|
Debug.Assert(dest.Type is OperandType.FP32 or OperandType.FP64);
|
||||||
|
|
||||||
if (dest.Type == OperandType.FP32)
|
if (dest.Type == OperandType.FP32)
|
||||||
{
|
{
|
||||||
@@ -1723,7 +1722,7 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Debug.Assert(op1.Kind == OperandKind.Register || op1.Kind == OperandKind.Memory);
|
Debug.Assert(op1.Kind is OperandKind.Register or OperandKind.Memory);
|
||||||
Debug.Assert(op1.Kind == op2.Kind);
|
Debug.Assert(op1.Kind == op2.Kind);
|
||||||
Debug.Assert(op1.Value == op2.Value);
|
Debug.Assert(op1.Value == op2.Value);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -312,9 +312,9 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
|
|
||||||
case Instruction.Extended:
|
case Instruction.Extended:
|
||||||
{
|
{
|
||||||
bool isBlend = node.Intrinsic == Intrinsic.X86Blendvpd ||
|
bool isBlend = node.Intrinsic is Intrinsic.X86Blendvpd
|
||||||
node.Intrinsic == Intrinsic.X86Blendvps ||
|
or Intrinsic.X86Blendvps
|
||||||
node.Intrinsic == Intrinsic.X86Pblendvb;
|
or Intrinsic.X86Pblendvb;
|
||||||
|
|
||||||
// BLENDVPD, BLENDVPS, PBLENDVB last operand is always implied to be XMM0 when VEX is not supported.
|
// BLENDVPD, BLENDVPS, PBLENDVB last operand is always implied to be XMM0 when VEX is not supported.
|
||||||
// SHA256RNDS2 always has an implied XMM0 as a last operand.
|
// SHA256RNDS2 always has an implied XMM0 as a last operand.
|
||||||
@@ -513,8 +513,8 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
Operand dest = node.Destination;
|
Operand dest = node.Destination;
|
||||||
Operand source = node.GetSource(0);
|
Operand source = node.GetSource(0);
|
||||||
|
|
||||||
Debug.Assert(dest.Type == OperandType.FP32 ||
|
Debug.Assert(dest.Type is OperandType.FP32 or OperandType.FP64,
|
||||||
dest.Type == OperandType.FP64, $"Invalid destination type \"{dest.Type}\".");
|
$"Invalid destination type \"{dest.Type}\".");
|
||||||
|
|
||||||
Operation currentNode = node;
|
Operation currentNode = node;
|
||||||
|
|
||||||
@@ -761,7 +761,7 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
|
|
||||||
var compType = (Comparison)comp.AsInt32();
|
var compType = (Comparison)comp.AsInt32();
|
||||||
|
|
||||||
return compType == Comparison.Equal || compType == Comparison.NotEqual;
|
return compType is Comparison.Equal or Comparison.NotEqual;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -248,12 +248,12 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
|
|
||||||
private static bool IsMemoryLoadOrStore(Instruction inst)
|
private static bool IsMemoryLoadOrStore(Instruction inst)
|
||||||
{
|
{
|
||||||
return inst == Instruction.Load ||
|
return inst is Instruction.Load
|
||||||
inst == Instruction.Load16 ||
|
or Instruction.Load16
|
||||||
inst == Instruction.Load8 ||
|
or Instruction.Load8
|
||||||
inst == Instruction.Store ||
|
or Instruction.Store
|
||||||
inst == Instruction.Store16 ||
|
or Instruction.Store16
|
||||||
inst == Instruction.Store8;
|
or Instruction.Store8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -254,8 +254,7 @@ namespace ARMeilleure.Decoders
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Compare and branch instructions are always conditional.
|
// Compare and branch instructions are always conditional.
|
||||||
if (opCode.Instruction.Name == InstName.Cbz ||
|
if (opCode.Instruction.Name is InstName.Cbz or InstName.Cbnz)
|
||||||
opCode.Instruction.Name == InstName.Cbnz)
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -284,7 +283,7 @@ namespace ARMeilleure.Decoders
|
|||||||
// register (Rt == 15 or (mask & (1 << 15)) != 0), and cases where there is
|
// register (Rt == 15 or (mask & (1 << 15)) != 0), and cases where there is
|
||||||
// a write back to PC (wback == true && Rn == 15), however the later may
|
// a write back to PC (wback == true && Rn == 15), however the later may
|
||||||
// be "undefined" depending on the CPU, so compilers should not produce that.
|
// be "undefined" depending on the CPU, so compilers should not produce that.
|
||||||
if (opCode is IOpCode32Mem || opCode is IOpCode32MemMult)
|
if (opCode is IOpCode32Mem or IOpCode32MemMult)
|
||||||
{
|
{
|
||||||
int rt, rn;
|
int rt, rn;
|
||||||
|
|
||||||
@@ -326,15 +325,12 @@ namespace ARMeilleure.Decoders
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Explicit branch instructions.
|
// Explicit branch instructions.
|
||||||
return opCode is IOpCode32BImm ||
|
return opCode is IOpCode32BImm or IOpCode32BReg;
|
||||||
opCode is IOpCode32BReg;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsCall(OpCode opCode)
|
private static bool IsCall(OpCode opCode)
|
||||||
{
|
{
|
||||||
return opCode.Instruction.Name == InstName.Bl ||
|
return opCode.Instruction.Name is InstName.Bl or InstName.Blr or InstName.Blx;
|
||||||
opCode.Instruction.Name == InstName.Blr ||
|
|
||||||
opCode.Instruction.Name == InstName.Blx;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsException(OpCode opCode)
|
private static bool IsException(OpCode opCode)
|
||||||
@@ -344,9 +340,7 @@ namespace ARMeilleure.Decoders
|
|||||||
|
|
||||||
private static bool IsTrap(OpCode opCode)
|
private static bool IsTrap(OpCode opCode)
|
||||||
{
|
{
|
||||||
return opCode.Instruction.Name == InstName.Brk ||
|
return opCode.Instruction.Name is InstName.Brk or InstName.Trap or InstName.Und;
|
||||||
opCode.Instruction.Name == InstName.Trap ||
|
|
||||||
opCode.Instruction.Name == InstName.Und;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static OpCode DecodeOpCode(IMemoryManager memory, ulong address, ExecutionMode mode)
|
public static OpCode DecodeOpCode(IMemoryManager memory, ulong address, ExecutionMode mode)
|
||||||
|
|||||||
@@ -28,8 +28,7 @@ namespace ARMeilleure.Decoders
|
|||||||
MemOp type = WBack ? (MemOp)((opCode >> 10) & 3) : MemOp.Unsigned;
|
MemOp type = WBack ? (MemOp)((opCode >> 10) & 3) : MemOp.Unsigned;
|
||||||
|
|
||||||
PostIdx = type == MemOp.PostIndexed;
|
PostIdx = type == MemOp.PostIndexed;
|
||||||
Unscaled = type == MemOp.Unscaled ||
|
Unscaled = type is MemOp.Unscaled or MemOp.Unprivileged;
|
||||||
type == MemOp.Unprivileged;
|
|
||||||
|
|
||||||
// Unscaled and Unprivileged doesn't write back,
|
// Unscaled and Unprivileged doesn't write back,
|
||||||
// but they do use the 9-bits Signed Immediate.
|
// but they do use the 9-bits Signed Immediate.
|
||||||
|
|||||||
@@ -235,8 +235,7 @@ namespace ARMeilleure.Diagnostics
|
|||||||
{
|
{
|
||||||
_builder.Append('.').Append(operation.Intrinsic);
|
_builder.Append('.').Append(operation.Intrinsic);
|
||||||
}
|
}
|
||||||
else if (operation.Instruction == Instruction.BranchIf ||
|
else if (operation.Instruction is Instruction.BranchIf or Instruction.Compare)
|
||||||
operation.Instruction == Instruction.Compare)
|
|
||||||
{
|
{
|
||||||
comparison = true;
|
comparison = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
context.LoadFromContext();
|
context.LoadFromContext();
|
||||||
|
|
||||||
context.Return(Const(op.Address));
|
InstEmitFlowHelper.EmitReturn(context, Const(op.Address));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Svc(ArmEmitterContext context)
|
public static void Svc(ArmEmitterContext context)
|
||||||
@@ -49,7 +49,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
context.LoadFromContext();
|
context.LoadFromContext();
|
||||||
|
|
||||||
context.Return(Const(op.Address));
|
InstEmitFlowHelper.EmitReturn(context, Const(op.Address));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
context.LoadFromContext();
|
context.LoadFromContext();
|
||||||
|
|
||||||
context.Return(Const(context.CurrOp.Address));
|
InstEmitFlowHelper.EmitReturn(context, Const(context.CurrOp.Address));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ namespace ARMeilleure.Instructions
|
|||||||
{
|
{
|
||||||
OpCodeBReg op = (OpCodeBReg)context.CurrOp;
|
OpCodeBReg op = (OpCodeBReg)context.CurrOp;
|
||||||
|
|
||||||
context.Return(GetIntOrZR(context, op.Rn));
|
EmitReturn(context, GetIntOrZR(context, op.Rn));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Tbnz(ArmEmitterContext context) => EmitTb(context, onNotZero: true);
|
public static void Tbnz(ArmEmitterContext context) => EmitTb(context, onNotZero: true);
|
||||||
|
|||||||
@@ -12,6 +12,10 @@ namespace ARMeilleure.Instructions
|
|||||||
{
|
{
|
||||||
static class InstEmitFlowHelper
|
static class InstEmitFlowHelper
|
||||||
{
|
{
|
||||||
|
// How many calls we can have in our call stack before we give up and return to the dispatcher.
|
||||||
|
// This prevents stack overflows caused by deep recursive calls.
|
||||||
|
private const int MaxCallDepth = 200;
|
||||||
|
|
||||||
public static void EmitCondBranch(ArmEmitterContext context, Operand target, Condition cond)
|
public static void EmitCondBranch(ArmEmitterContext context, Operand target, Condition cond)
|
||||||
{
|
{
|
||||||
if (cond != Condition.Al)
|
if (cond != Condition.Al)
|
||||||
@@ -142,6 +146,12 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
public static void EmitCall(ArmEmitterContext context, ulong immediate)
|
public static void EmitCall(ArmEmitterContext context, ulong immediate)
|
||||||
{
|
{
|
||||||
|
if (context.IsSingleStep)
|
||||||
|
{
|
||||||
|
context.Return(Const(immediate));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
bool isRecursive = immediate == context.EntryAddress;
|
bool isRecursive = immediate == context.EntryAddress;
|
||||||
|
|
||||||
if (isRecursive)
|
if (isRecursive)
|
||||||
@@ -156,12 +166,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
public static void EmitVirtualCall(ArmEmitterContext context, Operand target)
|
public static void EmitVirtualCall(ArmEmitterContext context, Operand target)
|
||||||
{
|
{
|
||||||
EmitTableBranch(context, target, isJump: false);
|
if (context.IsSingleStep)
|
||||||
}
|
|
||||||
|
|
||||||
public static void EmitVirtualJump(ArmEmitterContext context, Operand target, bool isReturn)
|
|
||||||
{
|
|
||||||
if (isReturn)
|
|
||||||
{
|
{
|
||||||
if (target.Type == OperandType.I32)
|
if (target.Type == OperandType.I32)
|
||||||
{
|
{
|
||||||
@@ -171,11 +176,36 @@ namespace ARMeilleure.Instructions
|
|||||||
context.Return(target);
|
context.Return(target);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
EmitTableBranch(context, target, isJump: false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void EmitVirtualJump(ArmEmitterContext context, Operand target, bool isReturn)
|
||||||
|
{
|
||||||
|
if (isReturn || context.IsSingleStep)
|
||||||
|
{
|
||||||
|
EmitReturn(context, target);
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
EmitTableBranch(context, target, isJump: true);
|
EmitTableBranch(context, target, isJump: true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void EmitReturn(ArmEmitterContext context, Operand target)
|
||||||
|
{
|
||||||
|
Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
|
||||||
|
DecreaseCallDepth(context, nativeContext);
|
||||||
|
|
||||||
|
if (target.Type == OperandType.I32)
|
||||||
|
{
|
||||||
|
target = context.ZeroExtend32(OperandType.I64, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
context.Return(target);
|
||||||
|
}
|
||||||
|
|
||||||
private static void EmitTableBranch(ArmEmitterContext context, Operand guestAddress, bool isJump)
|
private static void EmitTableBranch(ArmEmitterContext context, Operand guestAddress, bool isJump)
|
||||||
{
|
{
|
||||||
context.StoreToContext();
|
context.StoreToContext();
|
||||||
@@ -238,6 +268,8 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
if (isJump)
|
if (isJump)
|
||||||
{
|
{
|
||||||
|
DecreaseCallDepth(context, nativeContext);
|
||||||
|
|
||||||
context.Tailcall(hostAddress, nativeContext);
|
context.Tailcall(hostAddress, nativeContext);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -259,8 +291,42 @@ namespace ARMeilleure.Instructions
|
|||||||
Operand lblContinue = context.GetLabel(nextAddr.Value);
|
Operand lblContinue = context.GetLabel(nextAddr.Value);
|
||||||
context.BranchIf(lblContinue, returnAddress, nextAddr, Comparison.Equal, BasicBlockFrequency.Cold);
|
context.BranchIf(lblContinue, returnAddress, nextAddr, Comparison.Equal, BasicBlockFrequency.Cold);
|
||||||
|
|
||||||
|
DecreaseCallDepth(context, nativeContext);
|
||||||
|
|
||||||
context.Return(returnAddress);
|
context.Return(returnAddress);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void EmitCallDepthCheckAndIncrement(EmitterContext context, Operand guestAddress)
|
||||||
|
{
|
||||||
|
if (!Optimizations.EnableDeepCallRecursionProtection)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
|
||||||
|
Operand callDepthAddr = context.Add(nativeContext, Const((ulong)NativeContext.GetCallDepthOffset()));
|
||||||
|
Operand currentCallDepth = context.Load(OperandType.I32, callDepthAddr);
|
||||||
|
Operand lblDoCall = Label();
|
||||||
|
|
||||||
|
context.BranchIf(lblDoCall, currentCallDepth, Const(MaxCallDepth), Comparison.LessUI);
|
||||||
|
context.Store(callDepthAddr, context.Subtract(currentCallDepth, Const(1)));
|
||||||
|
context.Return(guestAddress);
|
||||||
|
|
||||||
|
context.MarkLabel(lblDoCall);
|
||||||
|
context.Store(callDepthAddr, context.Add(currentCallDepth, Const(1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void DecreaseCallDepth(EmitterContext context, Operand nativeContext)
|
||||||
|
{
|
||||||
|
if (!Optimizations.EnableDeepCallRecursionProtection)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Operand callDepthAddr = context.Add(nativeContext, Const((ulong)NativeContext.GetCallDepthOffset()));
|
||||||
|
Operand currentCallDepth = context.Load(OperandType.I32, callDepthAddr);
|
||||||
|
context.Store(callDepthAddr, context.Subtract(currentCallDepth, Const(1)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
if (pair)
|
if (pair)
|
||||||
{
|
{
|
||||||
Debug.Assert(op.Size == 2 || op.Size == 3, "Invalid size for pairwise store.");
|
Debug.Assert(op.Size is 2 or 3, "Invalid size for pairwise store.");
|
||||||
|
|
||||||
Operand t2 = GetIntOrZR(context, op.Rt2);
|
Operand t2 = GetIntOrZR(context, op.Rt2);
|
||||||
|
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ namespace ARMeilleure.Instructions
|
|||||||
{
|
{
|
||||||
Operand value = GetInt(context, rt);
|
Operand value = GetInt(context, rt);
|
||||||
|
|
||||||
if (ext == Extension.Sx32 || ext == Extension.Sx64)
|
if (ext is Extension.Sx32 or Extension.Sx64)
|
||||||
{
|
{
|
||||||
OperandType destType = ext == Extension.Sx64 ? OperandType.I64 : OperandType.I32;
|
OperandType destType = ext == Extension.Sx64 ? OperandType.I64 : OperandType.I32;
|
||||||
|
|
||||||
@@ -124,8 +124,7 @@ namespace ARMeilleure.Instructions
|
|||||||
private static bool IsSimd(ArmEmitterContext context)
|
private static bool IsSimd(ArmEmitterContext context)
|
||||||
{
|
{
|
||||||
return context.CurrOp is IOpCodeSimd &&
|
return context.CurrOp is IOpCodeSimd &&
|
||||||
!(context.CurrOp is OpCodeSimdMemMs ||
|
!(context.CurrOp is OpCodeSimdMemMs or OpCodeSimdMemSs);
|
||||||
context.CurrOp is OpCodeSimdMemSs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Operand EmitReadInt(ArmEmitterContext context, Operand address, int size)
|
public static Operand EmitReadInt(ArmEmitterContext context, Operand address, int size)
|
||||||
|
|||||||
@@ -1119,7 +1119,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
private static Operand EmitFPConvert(ArmEmitterContext context, Operand value, int size, bool signed)
|
private static Operand EmitFPConvert(ArmEmitterContext context, Operand value, int size, bool signed)
|
||||||
{
|
{
|
||||||
Debug.Assert(value.Type == OperandType.I32 || value.Type == OperandType.I64);
|
Debug.Assert(value.Type is OperandType.I32 or OperandType.I64);
|
||||||
Debug.Assert((uint)size < 2);
|
Debug.Assert((uint)size < 2);
|
||||||
|
|
||||||
OperandType type = size == 0 ? OperandType.FP32 : OperandType.FP64;
|
OperandType type = size == 0 ? OperandType.FP32 : OperandType.FP64;
|
||||||
@@ -1136,7 +1136,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
private static Operand EmitScalarFcvts(ArmEmitterContext context, Operand value, int fBits)
|
private static Operand EmitScalarFcvts(ArmEmitterContext context, Operand value, int fBits)
|
||||||
{
|
{
|
||||||
Debug.Assert(value.Type == OperandType.FP32 || value.Type == OperandType.FP64);
|
Debug.Assert(value.Type is OperandType.FP32 or OperandType.FP64);
|
||||||
|
|
||||||
value = EmitF2iFBitsMul(context, value, fBits);
|
value = EmitF2iFBitsMul(context, value, fBits);
|
||||||
|
|
||||||
@@ -1160,7 +1160,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
private static Operand EmitScalarFcvtu(ArmEmitterContext context, Operand value, int fBits)
|
private static Operand EmitScalarFcvtu(ArmEmitterContext context, Operand value, int fBits)
|
||||||
{
|
{
|
||||||
Debug.Assert(value.Type == OperandType.FP32 || value.Type == OperandType.FP64);
|
Debug.Assert(value.Type is OperandType.FP32 or OperandType.FP64);
|
||||||
|
|
||||||
value = EmitF2iFBitsMul(context, value, fBits);
|
value = EmitF2iFBitsMul(context, value, fBits);
|
||||||
|
|
||||||
@@ -1184,7 +1184,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
private static Operand EmitF2iFBitsMul(ArmEmitterContext context, Operand value, int fBits)
|
private static Operand EmitF2iFBitsMul(ArmEmitterContext context, Operand value, int fBits)
|
||||||
{
|
{
|
||||||
Debug.Assert(value.Type == OperandType.FP32 || value.Type == OperandType.FP64);
|
Debug.Assert(value.Type is OperandType.FP32 or OperandType.FP64);
|
||||||
|
|
||||||
if (fBits == 0)
|
if (fBits == 0)
|
||||||
{
|
{
|
||||||
@@ -1203,7 +1203,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
private static Operand EmitI2fFBitsMul(ArmEmitterContext context, Operand value, int fBits)
|
private static Operand EmitI2fFBitsMul(ArmEmitterContext context, Operand value, int fBits)
|
||||||
{
|
{
|
||||||
Debug.Assert(value.Type == OperandType.FP32 || value.Type == OperandType.FP64);
|
Debug.Assert(value.Type is OperandType.FP32 or OperandType.FP64);
|
||||||
|
|
||||||
if (fBits == 0)
|
if (fBits == 0)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -635,7 +635,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
private static Operand EmitFPConvert(ArmEmitterContext context, Operand value, OperandType type, bool signed)
|
private static Operand EmitFPConvert(ArmEmitterContext context, Operand value, OperandType type, bool signed)
|
||||||
{
|
{
|
||||||
Debug.Assert(value.Type == OperandType.I32 || value.Type == OperandType.I64);
|
Debug.Assert(value.Type is OperandType.I32 or OperandType.I64);
|
||||||
|
|
||||||
if (signed)
|
if (signed)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -363,7 +363,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
public static Operand EmitCountSetBits8(ArmEmitterContext context, Operand op) // "size" is 8 (SIMD&FP Inst.).
|
public static Operand EmitCountSetBits8(ArmEmitterContext context, Operand op) // "size" is 8 (SIMD&FP Inst.).
|
||||||
{
|
{
|
||||||
Debug.Assert(op.Type == OperandType.I32 || op.Type == OperandType.I64);
|
Debug.Assert(op.Type is OperandType.I32 or OperandType.I64);
|
||||||
|
|
||||||
Operand op0 = context.Subtract(op, context.BitwiseAnd(context.ShiftRightUI(op, Const(1)), Const(op.Type, 0x55L)));
|
Operand op0 = context.Subtract(op, context.BitwiseAnd(context.ShiftRightUI(op, Const(1)), Const(op.Type, 0x55L)));
|
||||||
|
|
||||||
@@ -489,7 +489,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
public static Operand EmitRoundByRMode(ArmEmitterContext context, Operand op)
|
public static Operand EmitRoundByRMode(ArmEmitterContext context, Operand op)
|
||||||
{
|
{
|
||||||
Debug.Assert(op.Type == OperandType.FP32 || op.Type == OperandType.FP64);
|
Debug.Assert(op.Type is OperandType.FP32 or OperandType.FP64);
|
||||||
|
|
||||||
Operand lbl1 = Label();
|
Operand lbl1 = Label();
|
||||||
Operand lbl2 = Label();
|
Operand lbl2 = Label();
|
||||||
@@ -1676,7 +1676,7 @@ namespace ARMeilleure.Instructions
|
|||||||
int eSize = 8 << size;
|
int eSize = 8 << size;
|
||||||
|
|
||||||
Debug.Assert(op.Type == OperandType.I64);
|
Debug.Assert(op.Type == OperandType.I64);
|
||||||
Debug.Assert(eSize == 8 || eSize == 16 || eSize == 32 || eSize == 64);
|
Debug.Assert(eSize is 8 or 16 or 32 or 64);
|
||||||
|
|
||||||
Operand lbl1 = Label();
|
Operand lbl1 = Label();
|
||||||
Operand lblEnd = Label();
|
Operand lblEnd = Label();
|
||||||
@@ -1709,7 +1709,7 @@ namespace ARMeilleure.Instructions
|
|||||||
int eSize = 8 << size;
|
int eSize = 8 << size;
|
||||||
|
|
||||||
Debug.Assert(op.Type == OperandType.I64);
|
Debug.Assert(op.Type == OperandType.I64);
|
||||||
Debug.Assert(eSize == 8 || eSize == 16 || eSize == 32 || eSize == 64);
|
Debug.Assert(eSize is 8 or 16 or 32 or 64);
|
||||||
|
|
||||||
Operand lblEnd = Label();
|
Operand lblEnd = Label();
|
||||||
|
|
||||||
@@ -1735,7 +1735,7 @@ namespace ARMeilleure.Instructions
|
|||||||
int eSizeDst = 8 << sizeDst;
|
int eSizeDst = 8 << sizeDst;
|
||||||
|
|
||||||
Debug.Assert(op.Type == OperandType.I64);
|
Debug.Assert(op.Type == OperandType.I64);
|
||||||
Debug.Assert(eSizeDst == 8 || eSizeDst == 16 || eSizeDst == 32);
|
Debug.Assert(eSizeDst is 8 or 16 or 32);
|
||||||
|
|
||||||
Operand lbl1 = Label();
|
Operand lbl1 = Label();
|
||||||
Operand lblEnd = Label();
|
Operand lblEnd = Label();
|
||||||
@@ -1768,7 +1768,7 @@ namespace ARMeilleure.Instructions
|
|||||||
int eSizeDst = 8 << sizeDst;
|
int eSizeDst = 8 << sizeDst;
|
||||||
|
|
||||||
Debug.Assert(op.Type == OperandType.I64);
|
Debug.Assert(op.Type == OperandType.I64);
|
||||||
Debug.Assert(eSizeDst == 8 || eSizeDst == 16 || eSizeDst == 32);
|
Debug.Assert(eSizeDst is 8 or 16 or 32);
|
||||||
|
|
||||||
Operand lblEnd = Label();
|
Operand lblEnd = Label();
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ namespace ARMeilleure.Instructions
|
|||||||
{
|
{
|
||||||
Debug.Assert(type != OperandType.V128);
|
Debug.Assert(type != OperandType.V128);
|
||||||
|
|
||||||
if (type == OperandType.FP64 || type == OperandType.I64)
|
if (type is OperandType.FP64 or OperandType.I64)
|
||||||
{
|
{
|
||||||
// From dreg.
|
// From dreg.
|
||||||
return context.VectorExtract(type, GetVecA32(reg >> 1), reg & 1);
|
return context.VectorExtract(type, GetVecA32(reg >> 1), reg & 1);
|
||||||
@@ -48,7 +48,7 @@ namespace ARMeilleure.Instructions
|
|||||||
Debug.Assert(value.Type != OperandType.V128);
|
Debug.Assert(value.Type != OperandType.V128);
|
||||||
|
|
||||||
Operand vec, insert;
|
Operand vec, insert;
|
||||||
if (value.Type == OperandType.FP64 || value.Type == OperandType.I64)
|
if (value.Type is OperandType.FP64 or OperandType.I64)
|
||||||
{
|
{
|
||||||
// From dreg.
|
// From dreg.
|
||||||
vec = GetVecA32(reg >> 1);
|
vec = GetVecA32(reg >> 1);
|
||||||
@@ -71,7 +71,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
public static void InsertScalar16(ArmEmitterContext context, int reg, bool top, Operand value)
|
public static void InsertScalar16(ArmEmitterContext context, int reg, bool top, Operand value)
|
||||||
{
|
{
|
||||||
Debug.Assert(value.Type == OperandType.FP32 || value.Type == OperandType.I32);
|
Debug.Assert(value.Type is OperandType.FP32 or OperandType.I32);
|
||||||
|
|
||||||
Operand vec, insert;
|
Operand vec, insert;
|
||||||
vec = GetVecA32(reg >> 2);
|
vec = GetVecA32(reg >> 2);
|
||||||
|
|||||||
@@ -1634,7 +1634,7 @@ namespace ARMeilleure.Instructions
|
|||||||
int eSize = 8 << size;
|
int eSize = 8 << size;
|
||||||
|
|
||||||
Debug.Assert(op.Type == OperandType.I64);
|
Debug.Assert(op.Type == OperandType.I64);
|
||||||
Debug.Assert(eSize == 8 || eSize == 16 || eSize == 32 || eSize == 64);
|
Debug.Assert(eSize is 8 or 16 or 32 or 64);
|
||||||
|
|
||||||
Operand res = context.AllocateLocal(OperandType.I64);
|
Operand res = context.AllocateLocal(OperandType.I64);
|
||||||
|
|
||||||
@@ -1657,7 +1657,7 @@ namespace ARMeilleure.Instructions
|
|||||||
int eSize = 8 << size;
|
int eSize = 8 << size;
|
||||||
|
|
||||||
Debug.Assert(op.Type == OperandType.I64);
|
Debug.Assert(op.Type == OperandType.I64);
|
||||||
Debug.Assert(eSize == 8 || eSize == 16 || eSize == 32 || eSize == 64);
|
Debug.Assert(eSize is 8 or 16 or 32 or 64);
|
||||||
|
|
||||||
Operand lblEnd = Label();
|
Operand lblEnd = Label();
|
||||||
|
|
||||||
@@ -1732,7 +1732,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
Debug.Assert(op.Type == OperandType.I64);
|
Debug.Assert(op.Type == OperandType.I64);
|
||||||
Debug.Assert(shiftLsB.Type == OperandType.I32);
|
Debug.Assert(shiftLsB.Type == OperandType.I32);
|
||||||
Debug.Assert(eSize == 8 || eSize == 16 || eSize == 32 || eSize == 64);
|
Debug.Assert(eSize is 8 or 16 or 32 or 64);
|
||||||
|
|
||||||
Operand lbl1 = Label();
|
Operand lbl1 = Label();
|
||||||
Operand lblEnd = Label();
|
Operand lblEnd = Label();
|
||||||
@@ -1769,7 +1769,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
Debug.Assert(op.Type == OperandType.I64);
|
Debug.Assert(op.Type == OperandType.I64);
|
||||||
Debug.Assert(shiftLsB.Type == OperandType.I32);
|
Debug.Assert(shiftLsB.Type == OperandType.I32);
|
||||||
Debug.Assert(eSize == 8 || eSize == 16 || eSize == 32 || eSize == 64);
|
Debug.Assert(eSize is 8 or 16 or 32 or 64);
|
||||||
|
|
||||||
Operand lbl1 = Label();
|
Operand lbl1 = Label();
|
||||||
Operand lbl2 = Label();
|
Operand lbl2 = Label();
|
||||||
|
|||||||
@@ -200,7 +200,11 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
ExecutionContext context = GetContext();
|
ExecutionContext context = GetContext();
|
||||||
|
|
||||||
context.CheckInterrupt();
|
// If debugging, we'll handle interrupts outside
|
||||||
|
if (!Optimizations.EnableDebugging)
|
||||||
|
{
|
||||||
|
context.CheckInterrupt();
|
||||||
|
}
|
||||||
|
|
||||||
Statistics.ResumeTimer();
|
Statistics.ResumeTimer();
|
||||||
|
|
||||||
|
|||||||
@@ -322,7 +322,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
float result;
|
float result;
|
||||||
|
|
||||||
if (type == FPType.SNaN || type == FPType.QNaN)
|
if (type is FPType.SNaN or FPType.QNaN)
|
||||||
{
|
{
|
||||||
if ((context.Fpcr & FPCR.Dn) != 0)
|
if ((context.Fpcr & FPCR.Dn) != 0)
|
||||||
{
|
{
|
||||||
@@ -498,7 +498,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
double result;
|
double result;
|
||||||
|
|
||||||
if (type == FPType.SNaN || type == FPType.QNaN)
|
if (type is FPType.SNaN or FPType.QNaN)
|
||||||
{
|
{
|
||||||
if ((context.Fpcr & FPCR.Dn) != 0)
|
if ((context.Fpcr & FPCR.Dn) != 0)
|
||||||
{
|
{
|
||||||
@@ -676,7 +676,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
ushort resultBits;
|
ushort resultBits;
|
||||||
|
|
||||||
if (type == FPType.SNaN || type == FPType.QNaN)
|
if (type is FPType.SNaN or FPType.QNaN)
|
||||||
{
|
{
|
||||||
if (altHp)
|
if (altHp)
|
||||||
{
|
{
|
||||||
@@ -1522,7 +1522,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
float result;
|
float result;
|
||||||
|
|
||||||
if (type == FPType.SNaN || type == FPType.QNaN)
|
if (type is FPType.SNaN or FPType.QNaN)
|
||||||
{
|
{
|
||||||
result = FPProcessNaN(type, op, context, fpcr);
|
result = FPProcessNaN(type, op, context, fpcr);
|
||||||
}
|
}
|
||||||
@@ -1689,7 +1689,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
float result;
|
float result;
|
||||||
|
|
||||||
if (type == FPType.SNaN || type == FPType.QNaN)
|
if (type is FPType.SNaN or FPType.QNaN)
|
||||||
{
|
{
|
||||||
result = FPProcessNaN(type, op, context, fpcr);
|
result = FPProcessNaN(type, op, context, fpcr);
|
||||||
}
|
}
|
||||||
@@ -1726,7 +1726,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
float result;
|
float result;
|
||||||
|
|
||||||
if (type == FPType.SNaN || type == FPType.QNaN)
|
if (type is FPType.SNaN or FPType.QNaN)
|
||||||
{
|
{
|
||||||
result = FPProcessNaN(type, op, context, fpcr);
|
result = FPProcessNaN(type, op, context, fpcr);
|
||||||
}
|
}
|
||||||
@@ -1920,7 +1920,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
float result;
|
float result;
|
||||||
|
|
||||||
if (type == FPType.SNaN || type == FPType.QNaN)
|
if (type is FPType.SNaN or FPType.QNaN)
|
||||||
{
|
{
|
||||||
result = FPProcessNaN(type, op, context, fpcr);
|
result = FPProcessNaN(type, op, context, fpcr);
|
||||||
}
|
}
|
||||||
@@ -2211,7 +2211,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
ushort resultBits;
|
ushort resultBits;
|
||||||
|
|
||||||
if (type == FPType.SNaN || type == FPType.QNaN)
|
if (type is FPType.SNaN or FPType.QNaN)
|
||||||
{
|
{
|
||||||
if (altHp)
|
if (altHp)
|
||||||
{
|
{
|
||||||
@@ -3057,7 +3057,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
double result;
|
double result;
|
||||||
|
|
||||||
if (type == FPType.SNaN || type == FPType.QNaN)
|
if (type is FPType.SNaN or FPType.QNaN)
|
||||||
{
|
{
|
||||||
result = FPProcessNaN(type, op, context, fpcr);
|
result = FPProcessNaN(type, op, context, fpcr);
|
||||||
}
|
}
|
||||||
@@ -3224,7 +3224,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
double result;
|
double result;
|
||||||
|
|
||||||
if (type == FPType.SNaN || type == FPType.QNaN)
|
if (type is FPType.SNaN or FPType.QNaN)
|
||||||
{
|
{
|
||||||
result = FPProcessNaN(type, op, context, fpcr);
|
result = FPProcessNaN(type, op, context, fpcr);
|
||||||
}
|
}
|
||||||
@@ -3261,7 +3261,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
double result;
|
double result;
|
||||||
|
|
||||||
if (type == FPType.SNaN || type == FPType.QNaN)
|
if (type is FPType.SNaN or FPType.QNaN)
|
||||||
{
|
{
|
||||||
result = FPProcessNaN(type, op, context, fpcr);
|
result = FPProcessNaN(type, op, context, fpcr);
|
||||||
}
|
}
|
||||||
@@ -3455,7 +3455,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
double result;
|
double result;
|
||||||
|
|
||||||
if (type == FPType.SNaN || type == FPType.QNaN)
|
if (type is FPType.SNaN or FPType.QNaN)
|
||||||
{
|
{
|
||||||
result = FPProcessNaN(type, op, context, fpcr);
|
result = FPProcessNaN(type, op, context, fpcr);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -446,7 +446,7 @@ namespace ARMeilleure.IntermediateRepresentation
|
|||||||
Data* data = null;
|
Data* data = null;
|
||||||
|
|
||||||
// If constant or register, then try to look up in the intern table before allocating.
|
// If constant or register, then try to look up in the intern table before allocating.
|
||||||
if (kind == OperandKind.Constant || kind == OperandKind.Register)
|
if (kind is OperandKind.Constant or OperandKind.Register)
|
||||||
{
|
{
|
||||||
uint hash = (uint)HashCode.Combine(kind, type, value);
|
uint hash = (uint)HashCode.Combine(kind, type, value);
|
||||||
|
|
||||||
|
|||||||
@@ -16,8 +16,7 @@ namespace ARMeilleure.IntermediateRepresentation
|
|||||||
{
|
{
|
||||||
public static bool IsInteger(this OperandType type)
|
public static bool IsInteger(this OperandType type)
|
||||||
{
|
{
|
||||||
return type == OperandType.I32 ||
|
return type is OperandType.I32 or OperandType.I64;
|
||||||
type == OperandType.I64;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static RegisterType ToRegisterType(this OperandType type)
|
public static RegisterType ToRegisterType(this OperandType type)
|
||||||
|
|||||||
@@ -47,12 +47,12 @@ namespace ARMeilleure.Memory
|
|||||||
{
|
{
|
||||||
public static bool IsHostMapped(this MemoryManagerType type)
|
public static bool IsHostMapped(this MemoryManagerType type)
|
||||||
{
|
{
|
||||||
return type == MemoryManagerType.HostMapped || type == MemoryManagerType.HostMappedUnsafe;
|
return type is MemoryManagerType.HostMapped or MemoryManagerType.HostMappedUnsafe;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool IsHostTracked(this MemoryManagerType type)
|
public static bool IsHostTracked(this MemoryManagerType type)
|
||||||
{
|
{
|
||||||
return type == MemoryManagerType.HostTracked || type == MemoryManagerType.HostTrackedUnsafe;
|
return type is MemoryManagerType.HostTracked or MemoryManagerType.HostTrackedUnsafe;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool IsHostMappedOrTracked(this MemoryManagerType type)
|
public static bool IsHostMappedOrTracked(this MemoryManagerType type)
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ namespace ARMeilleure
|
|||||||
|
|
||||||
public static bool AllowLcqInFunctionTable { get; set; } = true;
|
public static bool AllowLcqInFunctionTable { get; set; } = true;
|
||||||
public static bool UseUnmanagedDispatchLoop { get; set; } = true;
|
public static bool UseUnmanagedDispatchLoop { get; set; } = true;
|
||||||
|
public static bool EnableDebugging { get; set; } = false;
|
||||||
|
public static bool EnableDeepCallRecursionProtection { get; set; } = true;
|
||||||
|
|
||||||
public static bool UseAdvSimdIfAvailable { get; set; } = true;
|
public static bool UseAdvSimdIfAvailable { get; set; } = true;
|
||||||
public static bool UseArm64AesIfAvailable { get; set; } = true;
|
public static bool UseArm64AesIfAvailable { get; set; } = true;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using ARMeilleure.Memory;
|
using ARMeilleure.Memory;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace ARMeilleure.State
|
namespace ARMeilleure.State
|
||||||
{
|
{
|
||||||
@@ -11,7 +12,7 @@ namespace ARMeilleure.State
|
|||||||
|
|
||||||
internal IntPtr NativeContextPtr => _nativeContext.BasePtr;
|
internal IntPtr NativeContextPtr => _nativeContext.BasePtr;
|
||||||
|
|
||||||
private bool _interrupted;
|
internal bool Interrupted { get; private set; }
|
||||||
|
|
||||||
private readonly ICounter _counter;
|
private readonly ICounter _counter;
|
||||||
|
|
||||||
@@ -68,6 +69,8 @@ namespace ARMeilleure.State
|
|||||||
|
|
||||||
public bool IsAarch32 { get; set; }
|
public bool IsAarch32 { get; set; }
|
||||||
|
|
||||||
|
public ulong ThreadUid { get; set; }
|
||||||
|
|
||||||
internal ExecutionMode ExecutionMode
|
internal ExecutionMode ExecutionMode
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -93,14 +96,19 @@ namespace ARMeilleure.State
|
|||||||
|
|
||||||
private readonly ExceptionCallbackNoArgs _interruptCallback;
|
private readonly ExceptionCallbackNoArgs _interruptCallback;
|
||||||
private readonly ExceptionCallback _breakCallback;
|
private readonly ExceptionCallback _breakCallback;
|
||||||
|
private readonly ExceptionCallbackNoArgs _stepCallback;
|
||||||
private readonly ExceptionCallback _supervisorCallback;
|
private readonly ExceptionCallback _supervisorCallback;
|
||||||
private readonly ExceptionCallback _undefinedCallback;
|
private readonly ExceptionCallback _undefinedCallback;
|
||||||
|
|
||||||
|
internal int ShouldStep;
|
||||||
|
public ulong DebugPc { get; set; }
|
||||||
|
|
||||||
public ExecutionContext(
|
public ExecutionContext(
|
||||||
IJitMemoryAllocator allocator,
|
IJitMemoryAllocator allocator,
|
||||||
ICounter counter,
|
ICounter counter,
|
||||||
ExceptionCallbackNoArgs interruptCallback = null,
|
ExceptionCallbackNoArgs interruptCallback = null,
|
||||||
ExceptionCallback breakCallback = null,
|
ExceptionCallback breakCallback = null,
|
||||||
|
ExceptionCallbackNoArgs stepCallback = null,
|
||||||
ExceptionCallback supervisorCallback = null,
|
ExceptionCallback supervisorCallback = null,
|
||||||
ExceptionCallback undefinedCallback = null)
|
ExceptionCallback undefinedCallback = null)
|
||||||
{
|
{
|
||||||
@@ -108,6 +116,7 @@ namespace ARMeilleure.State
|
|||||||
_counter = counter;
|
_counter = counter;
|
||||||
_interruptCallback = interruptCallback;
|
_interruptCallback = interruptCallback;
|
||||||
_breakCallback = breakCallback;
|
_breakCallback = breakCallback;
|
||||||
|
_stepCallback = stepCallback;
|
||||||
_supervisorCallback = supervisorCallback;
|
_supervisorCallback = supervisorCallback;
|
||||||
_undefinedCallback = undefinedCallback;
|
_undefinedCallback = undefinedCallback;
|
||||||
|
|
||||||
@@ -128,11 +137,16 @@ namespace ARMeilleure.State
|
|||||||
public bool GetFPstateFlag(FPState flag) => _nativeContext.GetFPStateFlag(flag);
|
public bool GetFPstateFlag(FPState flag) => _nativeContext.GetFPStateFlag(flag);
|
||||||
public void SetFPstateFlag(FPState flag, bool value) => _nativeContext.SetFPStateFlag(flag, value);
|
public void SetFPstateFlag(FPState flag, bool value) => _nativeContext.SetFPStateFlag(flag, value);
|
||||||
|
|
||||||
|
internal void ResetCallDepth()
|
||||||
|
{
|
||||||
|
_nativeContext.ResetCallDepth();
|
||||||
|
}
|
||||||
|
|
||||||
internal void CheckInterrupt()
|
internal void CheckInterrupt()
|
||||||
{
|
{
|
||||||
if (_interrupted)
|
if (Interrupted)
|
||||||
{
|
{
|
||||||
_interrupted = false;
|
Interrupted = false;
|
||||||
|
|
||||||
_interruptCallback?.Invoke(this);
|
_interruptCallback?.Invoke(this);
|
||||||
}
|
}
|
||||||
@@ -142,16 +156,37 @@ namespace ARMeilleure.State
|
|||||||
|
|
||||||
public void RequestInterrupt()
|
public void RequestInterrupt()
|
||||||
{
|
{
|
||||||
_interrupted = true;
|
Interrupted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StepHandler()
|
||||||
|
{
|
||||||
|
_stepCallback?.Invoke(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RequestDebugStep()
|
||||||
|
{
|
||||||
|
Interlocked.Exchange(ref ShouldStep, 1);
|
||||||
|
RequestInterrupt();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void OnBreak(ulong address, int imm)
|
internal void OnBreak(ulong address, int imm)
|
||||||
{
|
{
|
||||||
|
if (Optimizations.EnableDebugging)
|
||||||
|
{
|
||||||
|
DebugPc = Pc;
|
||||||
|
}
|
||||||
|
|
||||||
_breakCallback?.Invoke(this, address, imm);
|
_breakCallback?.Invoke(this, address, imm);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void OnSupervisorCall(ulong address, int imm)
|
internal void OnSupervisorCall(ulong address, int imm)
|
||||||
{
|
{
|
||||||
|
if (Optimizations.EnableDebugging)
|
||||||
|
{
|
||||||
|
DebugPc = Pc;
|
||||||
|
}
|
||||||
|
|
||||||
_supervisorCallback?.Invoke(this, address, imm);
|
_supervisorCallback?.Invoke(this, address, imm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,14 @@ namespace ARMeilleure.State
|
|||||||
public ulong ExclusiveValueLow;
|
public ulong ExclusiveValueLow;
|
||||||
public ulong ExclusiveValueHigh;
|
public ulong ExclusiveValueHigh;
|
||||||
public int Running;
|
public int Running;
|
||||||
|
public int CallDepth;
|
||||||
public long Tpidr2El0;
|
public long Tpidr2El0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Precise PC value used for debugging.
|
||||||
|
/// This will only be set when Optimizations.EnableDebugging is true.
|
||||||
|
/// </summary>
|
||||||
|
public ulong DebugPrecisePc;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static NativeCtxStorage _dummyStorage = new();
|
private static NativeCtxStorage _dummyStorage = new();
|
||||||
@@ -39,6 +46,11 @@ namespace ARMeilleure.State
|
|||||||
|
|
||||||
public ulong GetPc()
|
public ulong GetPc()
|
||||||
{
|
{
|
||||||
|
if (Optimizations.EnableDebugging)
|
||||||
|
{
|
||||||
|
return GetStorage().DebugPrecisePc;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: More precise tracking of PC value.
|
// TODO: More precise tracking of PC value.
|
||||||
return GetStorage().DispatchAddress;
|
return GetStorage().DispatchAddress;
|
||||||
}
|
}
|
||||||
@@ -186,6 +198,8 @@ namespace ARMeilleure.State
|
|||||||
public bool GetRunning() => GetStorage().Running != 0;
|
public bool GetRunning() => GetStorage().Running != 0;
|
||||||
public void SetRunning(bool value) => GetStorage().Running = value ? 1 : 0;
|
public void SetRunning(bool value) => GetStorage().Running = value ? 1 : 0;
|
||||||
|
|
||||||
|
public void ResetCallDepth() => GetStorage().CallDepth = 0;
|
||||||
|
|
||||||
public unsafe static int GetRegisterOffset(Register reg)
|
public unsafe static int GetRegisterOffset(Register reg)
|
||||||
{
|
{
|
||||||
if (reg.Type == RegisterType.Integer)
|
if (reg.Type == RegisterType.Integer)
|
||||||
@@ -266,6 +280,16 @@ namespace ARMeilleure.State
|
|||||||
return StorageOffset(ref _dummyStorage, ref _dummyStorage.Running);
|
return StorageOffset(ref _dummyStorage, ref _dummyStorage.Running);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static int GetDebugPrecisePcOffset()
|
||||||
|
{
|
||||||
|
return StorageOffset(ref _dummyStorage, ref _dummyStorage.DebugPrecisePc);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int GetCallDepthOffset()
|
||||||
|
{
|
||||||
|
return StorageOffset(ref _dummyStorage, ref _dummyStorage.CallDepth);
|
||||||
|
}
|
||||||
|
|
||||||
private static int StorageOffset<T>(ref NativeCtxStorage storage, ref T target)
|
private static int StorageOffset<T>(ref NativeCtxStorage storage, ref T target)
|
||||||
{
|
{
|
||||||
return (int)Unsafe.ByteOffset(ref Unsafe.As<NativeCtxStorage, T>(ref storage), ref target);
|
return (int)Unsafe.ByteOffset(ref Unsafe.As<NativeCtxStorage, T>(ref storage), ref target);
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ namespace ARMeilleure.Translation
|
|||||||
public bool HighCq { get; }
|
public bool HighCq { get; }
|
||||||
public bool HasPtc { get; }
|
public bool HasPtc { get; }
|
||||||
public Aarch32Mode Mode { get; }
|
public Aarch32Mode Mode { get; }
|
||||||
|
public bool IsSingleStep { get; }
|
||||||
|
|
||||||
private int _ifThenBlockStateIndex = 0;
|
private int _ifThenBlockStateIndex = 0;
|
||||||
private Condition[] _ifThenBlockState = [];
|
private Condition[] _ifThenBlockState = [];
|
||||||
@@ -67,7 +68,8 @@ namespace ARMeilleure.Translation
|
|||||||
ulong entryAddress,
|
ulong entryAddress,
|
||||||
bool highCq,
|
bool highCq,
|
||||||
bool hasPtc,
|
bool hasPtc,
|
||||||
Aarch32Mode mode)
|
Aarch32Mode mode,
|
||||||
|
bool isSingleStep)
|
||||||
{
|
{
|
||||||
Memory = memory;
|
Memory = memory;
|
||||||
CountTable = countTable;
|
CountTable = countTable;
|
||||||
@@ -77,6 +79,7 @@ namespace ARMeilleure.Translation
|
|||||||
HighCq = highCq;
|
HighCq = highCq;
|
||||||
HasPtc = hasPtc;
|
HasPtc = hasPtc;
|
||||||
Mode = mode;
|
Mode = mode;
|
||||||
|
IsSingleStep = isSingleStep;
|
||||||
|
|
||||||
_labels = new Dictionary<ulong, Operand>();
|
_labels = new Dictionary<ulong, Operand>();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ namespace ARMeilleure.Translation
|
|||||||
|
|
||||||
private static string GetKey(MethodInfo info)
|
private static string GetKey(MethodInfo info)
|
||||||
{
|
{
|
||||||
return $"{info.DeclaringType.Name}.{info.Name}";
|
return $"{info.DeclaringType?.Name}.{info.Name}";
|
||||||
}
|
}
|
||||||
|
|
||||||
private static readonly SortedList<string, DelegateInfo> _delegates;
|
private static readonly SortedList<string, DelegateInfo> _delegates;
|
||||||
|
|||||||
@@ -668,8 +668,7 @@ namespace ARMeilleure.Translation
|
|||||||
Operation last = block.Operations.Last;
|
Operation last = block.Operations.Last;
|
||||||
|
|
||||||
return last != default &&
|
return last != default &&
|
||||||
(last.Instruction == Instruction.Return ||
|
last.Instruction is Instruction.Return or Instruction.Tailcall;
|
||||||
last.Instruction == Instruction.Tailcall);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ControlFlowGraph GetControlFlowGraph()
|
public ControlFlowGraph GetControlFlowGraph()
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
private const string OuterHeaderMagicString = "PTCohd\0\0";
|
private const string OuterHeaderMagicString = "PTCohd\0\0";
|
||||||
private const string InnerHeaderMagicString = "PTCihd\0\0";
|
private const string InnerHeaderMagicString = "PTCihd\0\0";
|
||||||
|
|
||||||
private const uint InternalVersion = 7008; //! To be incremented manually for each change to the ARMeilleure project.
|
private const uint InternalVersion = 7010; //! To be incremented manually for each change to the ARMeilleure project.
|
||||||
|
|
||||||
private const string ActualDir = "0";
|
private const string ActualDir = "0";
|
||||||
private const string BackupDir = "1";
|
private const string BackupDir = "1";
|
||||||
@@ -190,7 +190,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
_infosStream.Seek(0L, SeekOrigin.Begin);
|
_infosStream.Seek(0L, SeekOrigin.Begin);
|
||||||
bool foundBadFunction = false;
|
bool foundBadFunction = false;
|
||||||
|
|
||||||
for (int index = 0; index < GetEntriesCount(); index++)
|
for (int index = 0; index < _infosStream.Length / Unsafe.SizeOf<InfoEntry>(); index++)
|
||||||
{
|
{
|
||||||
InfoEntry infoEntry = DeserializeStructure<InfoEntry>(_infosStream);
|
InfoEntry infoEntry = DeserializeStructure<InfoEntry>(_infosStream);
|
||||||
foreach (ulong address in blacklist)
|
foreach (ulong address in blacklist)
|
||||||
@@ -244,56 +244,15 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
{
|
{
|
||||||
OuterHeader outerHeader = DeserializeStructure<OuterHeader>(compressedStream);
|
OuterHeader outerHeader = DeserializeStructure<OuterHeader>(compressedStream);
|
||||||
|
|
||||||
if (!outerHeader.IsHeaderValid())
|
if (!outerHeader.IsHeaderValid() ||
|
||||||
{
|
outerHeader.Magic != _outerHeaderMagic ||
|
||||||
InvalidateCompressedStream(compressedStream);
|
outerHeader.CacheFileVersion != InternalVersion ||
|
||||||
|
outerHeader.Endianness != GetEndianness() ||
|
||||||
return false;
|
outerHeader.FeatureInfo != GetFeatureInfo() ||
|
||||||
}
|
outerHeader.MemoryManagerMode != GetMemoryManagerMode() ||
|
||||||
|
outerHeader.OSPlatform != GetOSPlatform() ||
|
||||||
if (outerHeader.Magic != _outerHeaderMagic)
|
outerHeader.Architecture != (uint)RuntimeInformation.ProcessArchitecture ||
|
||||||
{
|
outerHeader.DebuggerMode != Optimizations.EnableDebugging)
|
||||||
InvalidateCompressedStream(compressedStream);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (outerHeader.CacheFileVersion != InternalVersion)
|
|
||||||
{
|
|
||||||
InvalidateCompressedStream(compressedStream);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (outerHeader.Endianness != GetEndianness())
|
|
||||||
{
|
|
||||||
InvalidateCompressedStream(compressedStream);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (outerHeader.FeatureInfo != GetFeatureInfo())
|
|
||||||
{
|
|
||||||
InvalidateCompressedStream(compressedStream);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (outerHeader.MemoryManagerMode != GetMemoryManagerMode())
|
|
||||||
{
|
|
||||||
InvalidateCompressedStream(compressedStream);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (outerHeader.OSPlatform != GetOSPlatform())
|
|
||||||
{
|
|
||||||
InvalidateCompressedStream(compressedStream);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (outerHeader.Architecture != (uint)RuntimeInformation.ProcessArchitecture)
|
|
||||||
{
|
{
|
||||||
InvalidateCompressedStream(compressedStream);
|
InvalidateCompressedStream(compressedStream);
|
||||||
|
|
||||||
@@ -324,14 +283,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
|
|
||||||
InnerHeader innerHeader = DeserializeStructure<InnerHeader>(stream);
|
InnerHeader innerHeader = DeserializeStructure<InnerHeader>(stream);
|
||||||
|
|
||||||
if (!innerHeader.IsHeaderValid())
|
if (!innerHeader.IsHeaderValid() || innerHeader.Magic != _innerHeaderMagic)
|
||||||
{
|
|
||||||
InvalidateCompressedStream(compressedStream);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (innerHeader.Magic != _innerHeaderMagic)
|
|
||||||
{
|
{
|
||||||
InvalidateCompressedStream(compressedStream);
|
InvalidateCompressedStream(compressedStream);
|
||||||
|
|
||||||
@@ -476,6 +428,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
MemoryManagerMode = GetMemoryManagerMode(),
|
MemoryManagerMode = GetMemoryManagerMode(),
|
||||||
OSPlatform = GetOSPlatform(),
|
OSPlatform = GetOSPlatform(),
|
||||||
Architecture = (uint)RuntimeInformation.ProcessArchitecture,
|
Architecture = (uint)RuntimeInformation.ProcessArchitecture,
|
||||||
|
DebuggerMode = Optimizations.EnableDebugging,
|
||||||
|
|
||||||
UncompressedStreamSize =
|
UncompressedStreamSize =
|
||||||
(long)Unsafe.SizeOf<InnerHeader>() +
|
(long)Unsafe.SizeOf<InnerHeader>() +
|
||||||
@@ -1072,7 +1025,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
return osPlatform;
|
return osPlatform;
|
||||||
}
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 86*/)]
|
[StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 87*/)]
|
||||||
private struct OuterHeader
|
private struct OuterHeader
|
||||||
{
|
{
|
||||||
public ulong Magic;
|
public ulong Magic;
|
||||||
@@ -1084,6 +1037,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
public byte MemoryManagerMode;
|
public byte MemoryManagerMode;
|
||||||
public uint OSPlatform;
|
public uint OSPlatform;
|
||||||
public uint Architecture;
|
public uint Architecture;
|
||||||
|
public bool DebuggerMode;
|
||||||
|
|
||||||
public long UncompressedStreamSize;
|
public long UncompressedStreamSize;
|
||||||
|
|
||||||
@@ -1166,8 +1120,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
|
|
||||||
public void Close()
|
public void Close()
|
||||||
{
|
{
|
||||||
if (State == PtcState.Enabled ||
|
if (State is PtcState.Enabled or PtcState.Continuing)
|
||||||
State == PtcState.Continuing)
|
|
||||||
{
|
{
|
||||||
State = PtcState.Closing;
|
State = PtcState.Closing;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -189,28 +189,10 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
{
|
{
|
||||||
OuterHeader outerHeader = DeserializeStructure<OuterHeader>(compressedStream);
|
OuterHeader outerHeader = DeserializeStructure<OuterHeader>(compressedStream);
|
||||||
|
|
||||||
if (!outerHeader.IsHeaderValid())
|
if (!outerHeader.IsHeaderValid() ||
|
||||||
{
|
outerHeader.Magic != _outerHeaderMagic ||
|
||||||
InvalidateCompressedStream(compressedStream);
|
outerHeader.InfoFileVersion != InternalVersion && !_migrateInternalVersions.Contains(outerHeader.InfoFileVersion) ||
|
||||||
|
outerHeader.Endianness != Ptc.GetEndianness())
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (outerHeader.Magic != _outerHeaderMagic)
|
|
||||||
{
|
|
||||||
InvalidateCompressedStream(compressedStream);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (outerHeader.InfoFileVersion != InternalVersion && !_migrateInternalVersions.Contains(outerHeader.InfoFileVersion))
|
|
||||||
{
|
|
||||||
InvalidateCompressedStream(compressedStream);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (outerHeader.Endianness != Ptc.GetEndianness())
|
|
||||||
{
|
{
|
||||||
InvalidateCompressedStream(compressedStream);
|
InvalidateCompressedStream(compressedStream);
|
||||||
|
|
||||||
@@ -465,8 +447,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
|
|
||||||
public void Start()
|
public void Start()
|
||||||
{
|
{
|
||||||
if (_ptc.State == PtcState.Enabled ||
|
if (_ptc.State is PtcState.Enabled or PtcState.Continuing)
|
||||||
_ptc.State == PtcState.Continuing)
|
|
||||||
{
|
{
|
||||||
Enabled = true;
|
Enabled = true;
|
||||||
|
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ namespace ARMeilleure.Translation
|
|||||||
// This is required because we have a implicit context load at the start of the function,
|
// This is required because we have a implicit context load at the start of the function,
|
||||||
// but if there is a jump to the start of the function, the context load would trash the modified values.
|
// but if there is a jump to the start of the function, the context load would trash the modified values.
|
||||||
// Here we insert a new entry block that will jump to the existing entry block.
|
// Here we insert a new entry block that will jump to the existing entry block.
|
||||||
BasicBlock newEntry = new BasicBlock(cfg.Blocks.Count);
|
BasicBlock newEntry = new(cfg.Blocks.Count);
|
||||||
|
|
||||||
cfg.UpdateEntry(newEntry);
|
cfg.UpdateEntry(newEntry);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -119,7 +119,25 @@ namespace ARMeilleure.Translation
|
|||||||
|
|
||||||
NativeInterface.RegisterThread(context, Memory, this);
|
NativeInterface.RegisterThread(context, Memory, this);
|
||||||
|
|
||||||
if (Optimizations.UseUnmanagedDispatchLoop)
|
if (Optimizations.EnableDebugging)
|
||||||
|
{
|
||||||
|
context.DebugPc = address;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (Interlocked.CompareExchange(ref context.ShouldStep, 0, 1) == 1)
|
||||||
|
{
|
||||||
|
context.DebugPc = Step(context, context.DebugPc);
|
||||||
|
context.StepHandler();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
context.DebugPc = ExecuteSingle(context, context.DebugPc);
|
||||||
|
}
|
||||||
|
context.CheckInterrupt();
|
||||||
|
}
|
||||||
|
while (context.Running && context.DebugPc != 0);
|
||||||
|
}
|
||||||
|
else if (Optimizations.UseUnmanagedDispatchLoop)
|
||||||
{
|
{
|
||||||
Stubs.DispatchLoop(context.NativeContextPtr, address);
|
Stubs.DispatchLoop(context.NativeContextPtr, address);
|
||||||
}
|
}
|
||||||
@@ -168,6 +186,7 @@ namespace ARMeilleure.Translation
|
|||||||
|
|
||||||
Statistics.StartTimer();
|
Statistics.StartTimer();
|
||||||
|
|
||||||
|
context.ResetCallDepth();
|
||||||
ulong nextAddr = func.Execute(Stubs.ContextWrapper, context);
|
ulong nextAddr = func.Execute(Stubs.ContextWrapper, context);
|
||||||
|
|
||||||
Statistics.StopTimer(address);
|
Statistics.StopTimer(address);
|
||||||
@@ -175,7 +194,7 @@ namespace ARMeilleure.Translation
|
|||||||
return nextAddr;
|
return nextAddr;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ulong Step(State.ExecutionContext context, ulong address)
|
private ulong Step(State.ExecutionContext context, ulong address)
|
||||||
{
|
{
|
||||||
TranslatedFunction func = Translate(address, context.ExecutionMode, highCq: false, singleStep: true);
|
TranslatedFunction func = Translate(address, context.ExecutionMode, highCq: false, singleStep: true);
|
||||||
|
|
||||||
@@ -186,6 +205,8 @@ namespace ARMeilleure.Translation
|
|||||||
return address;
|
return address;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
internal TranslatedFunction GetOrTranslate(ulong address, ExecutionMode mode)
|
internal TranslatedFunction GetOrTranslate(ulong address, ExecutionMode mode)
|
||||||
{
|
{
|
||||||
if (!Functions.TryGetValue(address, out TranslatedFunction func))
|
if (!Functions.TryGetValue(address, out TranslatedFunction func))
|
||||||
@@ -229,7 +250,8 @@ namespace ARMeilleure.Translation
|
|||||||
address,
|
address,
|
||||||
highCq,
|
highCq,
|
||||||
_ptc.State != PtcState.Disabled,
|
_ptc.State != PtcState.Disabled,
|
||||||
mode: Aarch32Mode.User);
|
mode: Aarch32Mode.User,
|
||||||
|
isSingleStep: singleStep);
|
||||||
|
|
||||||
Logger.StartPass(PassName.Decoding);
|
Logger.StartPass(PassName.Decoding);
|
||||||
|
|
||||||
@@ -239,6 +261,7 @@ namespace ARMeilleure.Translation
|
|||||||
|
|
||||||
Logger.StartPass(PassName.Translation);
|
Logger.StartPass(PassName.Translation);
|
||||||
|
|
||||||
|
InstEmitFlowHelper.EmitCallDepthCheckAndIncrement(context, Const(address));
|
||||||
EmitSynchronization(context);
|
EmitSynchronization(context);
|
||||||
|
|
||||||
if (blocks[0].Address != address)
|
if (blocks[0].Address != address)
|
||||||
@@ -367,9 +390,13 @@ namespace ARMeilleure.Translation
|
|||||||
|
|
||||||
if (block.Exit)
|
if (block.Exit)
|
||||||
{
|
{
|
||||||
// Left option here as it may be useful if we need to return to managed rather than tail call in
|
// Return to managed rather than tail call.
|
||||||
// future. (eg. for debug)
|
bool useReturns = Optimizations.EnableDebugging;
|
||||||
bool useReturns = false;
|
|
||||||
|
if (Optimizations.EnableDebugging)
|
||||||
|
{
|
||||||
|
EmitDebugPrecisePcUpdate(context, block.Address);
|
||||||
|
}
|
||||||
|
|
||||||
InstEmitFlowHelper.EmitVirtualJump(context, Const(block.Address), isReturn: useReturns);
|
InstEmitFlowHelper.EmitVirtualJump(context, Const(block.Address), isReturn: useReturns);
|
||||||
}
|
}
|
||||||
@@ -393,6 +420,11 @@ namespace ARMeilleure.Translation
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Optimizations.EnableDebugging)
|
||||||
|
{
|
||||||
|
EmitDebugPrecisePcUpdate(context, opCode.Address);
|
||||||
|
}
|
||||||
|
|
||||||
Operand lblPredicateSkip = default;
|
Operand lblPredicateSkip = default;
|
||||||
|
|
||||||
if (context.IsInIfThenBlock && context.CurrentIfThenBlockCond != Condition.Al)
|
if (context.IsInIfThenBlock && context.CurrentIfThenBlockCond != Condition.Al)
|
||||||
@@ -489,6 +521,14 @@ namespace ARMeilleure.Translation
|
|||||||
context.MarkLabel(lblExit);
|
context.MarkLabel(lblExit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal static void EmitDebugPrecisePcUpdate(EmitterContext context, ulong address)
|
||||||
|
{
|
||||||
|
long debugPrecisePcOffs = NativeContext.GetDebugPrecisePcOffset();
|
||||||
|
|
||||||
|
Operand debugPrecisePcAddr = context.Add(context.LoadArgument(OperandType.I64, 0), Const(debugPrecisePcOffs));
|
||||||
|
context.Store(debugPrecisePcAddr, Const(address));
|
||||||
|
}
|
||||||
|
|
||||||
public void InvalidateJitCacheRegion(ulong address, ulong size)
|
public void InvalidateJitCacheRegion(ulong address, ulong size)
|
||||||
{
|
{
|
||||||
ulong[] overlapAddresses = [];
|
ulong[] overlapAddresses = [];
|
||||||
|
|||||||
@@ -262,10 +262,18 @@ namespace ARMeilleure.Translation
|
|||||||
|
|
||||||
Operand runningAddress = context.Add(nativeContext, Const((ulong)NativeContext.GetRunningOffset()));
|
Operand runningAddress = context.Add(nativeContext, Const((ulong)NativeContext.GetRunningOffset()));
|
||||||
Operand dispatchAddress = context.Add(nativeContext, Const((ulong)NativeContext.GetDispatchAddressOffset()));
|
Operand dispatchAddress = context.Add(nativeContext, Const((ulong)NativeContext.GetDispatchAddressOffset()));
|
||||||
|
Operand callDepthAddress = context.Add(nativeContext, Const((ulong)NativeContext.GetCallDepthOffset()));
|
||||||
|
|
||||||
EmitSyncFpContext(context, nativeContext, true);
|
EmitSyncFpContext(context, nativeContext, true);
|
||||||
|
|
||||||
context.MarkLabel(beginLbl);
|
context.MarkLabel(beginLbl);
|
||||||
|
|
||||||
|
if (Optimizations.EnableDeepCallRecursionProtection)
|
||||||
|
{
|
||||||
|
// Reset the call depth counter, since this is our first guest function call.
|
||||||
|
context.Store(callDepthAddress, Const(0));
|
||||||
|
}
|
||||||
|
|
||||||
context.Store(dispatchAddress, guestAddress);
|
context.Store(dispatchAddress, guestAddress);
|
||||||
context.Copy(guestAddress, context.Call(Const((ulong)DispatchStub), OperandType.I64, nativeContext));
|
context.Copy(guestAddress, context.Call(Const((ulong)DispatchStub), OperandType.I64, nativeContext));
|
||||||
context.BranchIfFalse(endLbl, guestAddress);
|
context.BranchIfFalse(endLbl, guestAddress);
|
||||||
|
|||||||
@@ -178,7 +178,7 @@ namespace Ryujinx.Audio.Backends.OpenAL
|
|||||||
|
|
||||||
public bool SupportsChannelCount(uint channelCount)
|
public bool SupportsChannelCount(uint channelCount)
|
||||||
{
|
{
|
||||||
return channelCount == 1 || channelCount == 2 || channelCount == 6;
|
return channelCount is 1 or 2 or 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool SupportsDirection(Direction direction)
|
public bool SupportsDirection(Direction direction)
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ namespace Ryujinx.Audio.Backends.SoundIo.Native
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_onBackendDisconnectNative = (ctx, err) => _onBackendDisconnect(err);
|
_onBackendDisconnectNative = (_, err) => _onBackendDisconnect(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
GetContext().OnBackendDisconnected = Marshal.GetFunctionPointerForDelegate(_onBackendDisconnectNative);
|
GetContext().OnBackendDisconnected = Marshal.GetFunctionPointerForDelegate(_onBackendDisconnectNative);
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ namespace Ryujinx.Audio.Backends.SoundIo.Native
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_writeCallbackNative = (ctx, frameCountMin, frameCountMax) => _writeCallback(frameCountMin, frameCountMax);
|
_writeCallbackNative = (_, frameCountMin, frameCountMax) => _writeCallback(frameCountMin, frameCountMax);
|
||||||
}
|
}
|
||||||
|
|
||||||
GetOutContext().WriteCallback = Marshal.GetFunctionPointerForDelegate(_writeCallbackNative);
|
GetOutContext().WriteCallback = Marshal.GetFunctionPointerForDelegate(_writeCallbackNative);
|
||||||
|
|||||||
@@ -11,15 +11,15 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.dll" Condition="'$(RuntimeIdentifier)' == 'win-x64' OR '$(RuntimeIdentifier)' == 'win-arm64'">
|
<ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.dll" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'linux-arm64' AND '$(RuntimeIdentifier)' != 'osx-x64' AND '$(RuntimeIdentifier)' != 'osx-arm64'">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
<TargetPath>libsoundio.dll</TargetPath>
|
<TargetPath>libsoundio.dll</TargetPath>
|
||||||
</ContentWithTargetPath>
|
</ContentWithTargetPath>
|
||||||
<ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.dylib" Condition="'$(RuntimeIdentifier)' == 'osx-x64' OR '$(RuntimeIdentifier)' == 'osx-arm64'">
|
<ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.dylib" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'linux-arm64' AND '$(RuntimeIdentifier)' != 'win-x64' AND '$(RuntimeIdentifier)' != 'win-arm64'">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
<TargetPath>libsoundio.dylib</TargetPath>
|
<TargetPath>libsoundio.dylib</TargetPath>
|
||||||
</ContentWithTargetPath>
|
</ContentWithTargetPath>
|
||||||
<ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.so" Condition="'$(RuntimeIdentifier)' == 'linux-x64'">
|
<ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.so" Condition="'$(RuntimeIdentifier)' != 'linux-arm64' AND '$(RuntimeIdentifier)' != 'win-x64' AND '$(RuntimeIdentifier)' != 'win-arm64' AND '$(RuntimeIdentifier)' != 'osx-x64' AND '$(RuntimeIdentifier)' != 'osx-arm64'">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
<TargetPath>libsoundio.so</TargetPath>
|
<TargetPath>libsoundio.so</TargetPath>
|
||||||
</ContentWithTargetPath>
|
</ContentWithTargetPath>
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ namespace Ryujinx.Audio.Backends.SoundIo
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
context = SoundIoContext.Create();
|
context = SoundIoContext.Create();
|
||||||
context.OnBackendDisconnect = err =>
|
context.OnBackendDisconnect = _ =>
|
||||||
{
|
{
|
||||||
backendDisconnected = true;
|
backendDisconnected = true;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -162,7 +162,7 @@ namespace Ryujinx.Audio.Backends.CompatLayer
|
|||||||
|
|
||||||
public bool SupportsChannelCount(uint channelCount)
|
public bool SupportsChannelCount(uint channelCount)
|
||||||
{
|
{
|
||||||
return channelCount == 1 || channelCount == 2 || channelCount == 6;
|
return channelCount is 1 or 2 or 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool SupportsSampleFormat(SampleFormat sampleFormat)
|
public bool SupportsSampleFormat(SampleFormat sampleFormat)
|
||||||
@@ -184,7 +184,7 @@ namespace Ryujinx.Audio.Backends.CompatLayer
|
|||||||
|
|
||||||
public bool SupportsDirection(Direction direction)
|
public bool SupportsDirection(Direction direction)
|
||||||
{
|
{
|
||||||
return direction == Direction.Input || direction == Direction.Output;
|
return direction is Direction.Input or Direction.Output;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,12 +81,12 @@ namespace Ryujinx.Audio.Backends.Dummy
|
|||||||
|
|
||||||
public bool SupportsDirection(Direction direction)
|
public bool SupportsDirection(Direction direction)
|
||||||
{
|
{
|
||||||
return direction == Direction.Output || direction == Direction.Input;
|
return direction is Direction.Output or Direction.Input;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool SupportsChannelCount(uint channelCount)
|
public bool SupportsChannelCount(uint channelCount)
|
||||||
{
|
{
|
||||||
return channelCount == 1 || channelCount == 2 || channelCount == 6;
|
return channelCount is 1 or 2 or 6;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ namespace Ryujinx.Audio.Common
|
|||||||
/// <returns>The state of the session</returns>
|
/// <returns>The state of the session</returns>
|
||||||
public AudioDeviceState GetState()
|
public AudioDeviceState GetState()
|
||||||
{
|
{
|
||||||
Debug.Assert(_state == AudioDeviceState.Started || _state == AudioDeviceState.Stopped);
|
Debug.Assert(_state is AudioDeviceState.Started or AudioDeviceState.Stopped);
|
||||||
|
|
||||||
return _state;
|
return _state;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,14 +81,14 @@ namespace Ryujinx.Audio.Renderer.Dsp
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private static short GetCoefficientAtIndex(ReadOnlySpan<short> coefficients, int index)
|
private static short GetCoefficientAtIndex(ReadOnlySpan<short> coefficients, int index)
|
||||||
{
|
{
|
||||||
if ((uint)index >= (uint)coefficients.Length)
|
if ((uint)index < (uint)coefficients.Length)
|
||||||
{
|
{
|
||||||
Logger.Error?.Print(LogClass.AudioRenderer, $"Out of bound read for coefficient at index {index}");
|
return coefficients[index];
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return coefficients[index];
|
Logger.Error?.Print(LogClass.AudioRenderer, $"Out of bound read for coefficient at index {index}");
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
|||||||
@@ -26,12 +26,15 @@ namespace Ryujinx.Audio.Renderer.Dsp
|
|||||||
ReadOnlySpan<float> inputBuffer,
|
ReadOnlySpan<float> inputBuffer,
|
||||||
uint sampleCount)
|
uint sampleCount)
|
||||||
{
|
{
|
||||||
float a0 = FixedPointHelper.ToFloat(parameter.Numerator[0], FixedPointPrecisionForParameter);
|
Span<short> numeratorSpan = parameter.Numerator.AsSpan();
|
||||||
float a1 = FixedPointHelper.ToFloat(parameter.Numerator[1], FixedPointPrecisionForParameter);
|
Span<short> denominatorSpan = parameter.Denominator.AsSpan();
|
||||||
float a2 = FixedPointHelper.ToFloat(parameter.Numerator[2], FixedPointPrecisionForParameter);
|
|
||||||
|
|
||||||
float b1 = FixedPointHelper.ToFloat(parameter.Denominator[0], FixedPointPrecisionForParameter);
|
float a0 = FixedPointHelper.ToFloat(numeratorSpan[0], FixedPointPrecisionForParameter);
|
||||||
float b2 = FixedPointHelper.ToFloat(parameter.Denominator[1], FixedPointPrecisionForParameter);
|
float a1 = FixedPointHelper.ToFloat(numeratorSpan[1], FixedPointPrecisionForParameter);
|
||||||
|
float a2 = FixedPointHelper.ToFloat(numeratorSpan[2], FixedPointPrecisionForParameter);
|
||||||
|
|
||||||
|
float b1 = FixedPointHelper.ToFloat(denominatorSpan[0], FixedPointPrecisionForParameter);
|
||||||
|
float b2 = FixedPointHelper.ToFloat(denominatorSpan[1], FixedPointPrecisionForParameter);
|
||||||
|
|
||||||
for (int i = 0; i < sampleCount; i++)
|
for (int i = 0; i < sampleCount; i++)
|
||||||
{
|
{
|
||||||
@@ -64,12 +67,15 @@ namespace Ryujinx.Audio.Renderer.Dsp
|
|||||||
uint sampleCount,
|
uint sampleCount,
|
||||||
float volume)
|
float volume)
|
||||||
{
|
{
|
||||||
float a0 = FixedPointHelper.ToFloat(parameter.Numerator[0], FixedPointPrecisionForParameter);
|
Span<short> numeratorSpan = parameter.Numerator.AsSpan();
|
||||||
float a1 = FixedPointHelper.ToFloat(parameter.Numerator[1], FixedPointPrecisionForParameter);
|
Span<short> denominatorSpan = parameter.Denominator.AsSpan();
|
||||||
float a2 = FixedPointHelper.ToFloat(parameter.Numerator[2], FixedPointPrecisionForParameter);
|
|
||||||
|
|
||||||
float b1 = FixedPointHelper.ToFloat(parameter.Denominator[0], FixedPointPrecisionForParameter);
|
float a0 = FixedPointHelper.ToFloat(numeratorSpan[0], FixedPointPrecisionForParameter);
|
||||||
float b2 = FixedPointHelper.ToFloat(parameter.Denominator[1], FixedPointPrecisionForParameter);
|
float a1 = FixedPointHelper.ToFloat(numeratorSpan[1], FixedPointPrecisionForParameter);
|
||||||
|
float a2 = FixedPointHelper.ToFloat(numeratorSpan[2], FixedPointPrecisionForParameter);
|
||||||
|
|
||||||
|
float b1 = FixedPointHelper.ToFloat(denominatorSpan[0], FixedPointPrecisionForParameter);
|
||||||
|
float b2 = FixedPointHelper.ToFloat(denominatorSpan[1], FixedPointPrecisionForParameter);
|
||||||
|
|
||||||
for (int i = 0; i < sampleCount; i++)
|
for (int i = 0; i < sampleCount; i++)
|
||||||
{
|
{
|
||||||
@@ -107,12 +113,15 @@ namespace Ryujinx.Audio.Renderer.Dsp
|
|||||||
float volume,
|
float volume,
|
||||||
float ramp)
|
float ramp)
|
||||||
{
|
{
|
||||||
float a0 = FixedPointHelper.ToFloat(parameter.Numerator[0], FixedPointPrecisionForParameter);
|
Span<short> numeratorSpan = parameter.Numerator.AsSpan();
|
||||||
float a1 = FixedPointHelper.ToFloat(parameter.Numerator[1], FixedPointPrecisionForParameter);
|
Span<short> denominatorSpan = parameter.Denominator.AsSpan();
|
||||||
float a2 = FixedPointHelper.ToFloat(parameter.Numerator[2], FixedPointPrecisionForParameter);
|
|
||||||
|
|
||||||
float b1 = FixedPointHelper.ToFloat(parameter.Denominator[0], FixedPointPrecisionForParameter);
|
float a0 = FixedPointHelper.ToFloat(numeratorSpan[0], FixedPointPrecisionForParameter);
|
||||||
float b2 = FixedPointHelper.ToFloat(parameter.Denominator[1], FixedPointPrecisionForParameter);
|
float a1 = FixedPointHelper.ToFloat(numeratorSpan[1], FixedPointPrecisionForParameter);
|
||||||
|
float a2 = FixedPointHelper.ToFloat(numeratorSpan[2], FixedPointPrecisionForParameter);
|
||||||
|
|
||||||
|
float b1 = FixedPointHelper.ToFloat(denominatorSpan[0], FixedPointPrecisionForParameter);
|
||||||
|
float b2 = FixedPointHelper.ToFloat(denominatorSpan[1], FixedPointPrecisionForParameter);
|
||||||
|
|
||||||
float mixState = 0f;
|
float mixState = 0f;
|
||||||
|
|
||||||
@@ -158,12 +167,15 @@ namespace Ryujinx.Audio.Renderer.Dsp
|
|||||||
|
|
||||||
ref BiquadFilterState state = ref states[stageIndex];
|
ref BiquadFilterState state = ref states[stageIndex];
|
||||||
|
|
||||||
float a0 = FixedPointHelper.ToFloat(parameter.Numerator[0], FixedPointPrecisionForParameter);
|
Span<short> numeratorSpan = parameter.Numerator.AsSpan();
|
||||||
float a1 = FixedPointHelper.ToFloat(parameter.Numerator[1], FixedPointPrecisionForParameter);
|
Span<short> denominatorSpan = parameter.Denominator.AsSpan();
|
||||||
float a2 = FixedPointHelper.ToFloat(parameter.Numerator[2], FixedPointPrecisionForParameter);
|
|
||||||
|
|
||||||
float b1 = FixedPointHelper.ToFloat(parameter.Denominator[0], FixedPointPrecisionForParameter);
|
float a0 = FixedPointHelper.ToFloat(numeratorSpan[0], FixedPointPrecisionForParameter);
|
||||||
float b2 = FixedPointHelper.ToFloat(parameter.Denominator[1], FixedPointPrecisionForParameter);
|
float a1 = FixedPointHelper.ToFloat(numeratorSpan[1], FixedPointPrecisionForParameter);
|
||||||
|
float a2 = FixedPointHelper.ToFloat(numeratorSpan[2], FixedPointPrecisionForParameter);
|
||||||
|
|
||||||
|
float b1 = FixedPointHelper.ToFloat(denominatorSpan[0], FixedPointPrecisionForParameter);
|
||||||
|
float b2 = FixedPointHelper.ToFloat(denominatorSpan[1], FixedPointPrecisionForParameter);
|
||||||
|
|
||||||
for (int i = 0; i < sampleCount; i++)
|
for (int i = 0; i < sampleCount; i++)
|
||||||
{
|
{
|
||||||
@@ -201,19 +213,25 @@ namespace Ryujinx.Audio.Renderer.Dsp
|
|||||||
uint sampleCount,
|
uint sampleCount,
|
||||||
float volume)
|
float volume)
|
||||||
{
|
{
|
||||||
float a00 = FixedPointHelper.ToFloat(parameter0.Numerator[0], FixedPointPrecisionForParameter);
|
Span<short> numerator0Span = parameter0.Numerator.AsSpan();
|
||||||
float a10 = FixedPointHelper.ToFloat(parameter0.Numerator[1], FixedPointPrecisionForParameter);
|
Span<short> numerator1Span = parameter1.Numerator.AsSpan();
|
||||||
float a20 = FixedPointHelper.ToFloat(parameter0.Numerator[2], FixedPointPrecisionForParameter);
|
Span<short> denominator0Span = parameter0.Denominator.AsSpan();
|
||||||
|
Span<short> denominator1Span = parameter1.Denominator.AsSpan();
|
||||||
|
|
||||||
float b10 = FixedPointHelper.ToFloat(parameter0.Denominator[0], FixedPointPrecisionForParameter);
|
|
||||||
float b20 = FixedPointHelper.ToFloat(parameter0.Denominator[1], FixedPointPrecisionForParameter);
|
|
||||||
|
|
||||||
float a01 = FixedPointHelper.ToFloat(parameter1.Numerator[0], FixedPointPrecisionForParameter);
|
float a00 = FixedPointHelper.ToFloat(numerator0Span[0], FixedPointPrecisionForParameter);
|
||||||
float a11 = FixedPointHelper.ToFloat(parameter1.Numerator[1], FixedPointPrecisionForParameter);
|
float a10 = FixedPointHelper.ToFloat(numerator0Span[1], FixedPointPrecisionForParameter);
|
||||||
float a21 = FixedPointHelper.ToFloat(parameter1.Numerator[2], FixedPointPrecisionForParameter);
|
float a20 = FixedPointHelper.ToFloat(numerator0Span[2], FixedPointPrecisionForParameter);
|
||||||
|
|
||||||
float b11 = FixedPointHelper.ToFloat(parameter1.Denominator[0], FixedPointPrecisionForParameter);
|
float b10 = FixedPointHelper.ToFloat(denominator0Span[0], FixedPointPrecisionForParameter);
|
||||||
float b21 = FixedPointHelper.ToFloat(parameter1.Denominator[1], FixedPointPrecisionForParameter);
|
float b20 = FixedPointHelper.ToFloat(denominator0Span[1], FixedPointPrecisionForParameter);
|
||||||
|
|
||||||
|
float a01 = FixedPointHelper.ToFloat(numerator1Span[0], FixedPointPrecisionForParameter);
|
||||||
|
float a11 = FixedPointHelper.ToFloat(numerator1Span[1], FixedPointPrecisionForParameter);
|
||||||
|
float a21 = FixedPointHelper.ToFloat(numerator1Span[2], FixedPointPrecisionForParameter);
|
||||||
|
|
||||||
|
float b11 = FixedPointHelper.ToFloat(denominator1Span[0], FixedPointPrecisionForParameter);
|
||||||
|
float b21 = FixedPointHelper.ToFloat(denominator1Span[1], FixedPointPrecisionForParameter);
|
||||||
|
|
||||||
for (int i = 0; i < sampleCount; i++)
|
for (int i = 0; i < sampleCount; i++)
|
||||||
{
|
{
|
||||||
@@ -261,19 +279,24 @@ namespace Ryujinx.Audio.Renderer.Dsp
|
|||||||
float volume,
|
float volume,
|
||||||
float ramp)
|
float ramp)
|
||||||
{
|
{
|
||||||
float a00 = FixedPointHelper.ToFloat(parameter0.Numerator[0], FixedPointPrecisionForParameter);
|
Span<short> numerator0Span = parameter0.Numerator.AsSpan();
|
||||||
float a10 = FixedPointHelper.ToFloat(parameter0.Numerator[1], FixedPointPrecisionForParameter);
|
Span<short> numerator1Span = parameter1.Numerator.AsSpan();
|
||||||
float a20 = FixedPointHelper.ToFloat(parameter0.Numerator[2], FixedPointPrecisionForParameter);
|
Span<short> denominator0Span = parameter0.Denominator.AsSpan();
|
||||||
|
Span<short> denominator1Span = parameter1.Denominator.AsSpan();
|
||||||
|
|
||||||
float b10 = FixedPointHelper.ToFloat(parameter0.Denominator[0], FixedPointPrecisionForParameter);
|
float a00 = FixedPointHelper.ToFloat(numerator0Span[0], FixedPointPrecisionForParameter);
|
||||||
float b20 = FixedPointHelper.ToFloat(parameter0.Denominator[1], FixedPointPrecisionForParameter);
|
float a10 = FixedPointHelper.ToFloat(numerator0Span[1], FixedPointPrecisionForParameter);
|
||||||
|
float a20 = FixedPointHelper.ToFloat(numerator0Span[2], FixedPointPrecisionForParameter);
|
||||||
|
|
||||||
float a01 = FixedPointHelper.ToFloat(parameter1.Numerator[0], FixedPointPrecisionForParameter);
|
float b10 = FixedPointHelper.ToFloat(denominator0Span[0], FixedPointPrecisionForParameter);
|
||||||
float a11 = FixedPointHelper.ToFloat(parameter1.Numerator[1], FixedPointPrecisionForParameter);
|
float b20 = FixedPointHelper.ToFloat(denominator0Span[1], FixedPointPrecisionForParameter);
|
||||||
float a21 = FixedPointHelper.ToFloat(parameter1.Numerator[2], FixedPointPrecisionForParameter);
|
|
||||||
|
|
||||||
float b11 = FixedPointHelper.ToFloat(parameter1.Denominator[0], FixedPointPrecisionForParameter);
|
float a01 = FixedPointHelper.ToFloat(numerator1Span[0], FixedPointPrecisionForParameter);
|
||||||
float b21 = FixedPointHelper.ToFloat(parameter1.Denominator[1], FixedPointPrecisionForParameter);
|
float a11 = FixedPointHelper.ToFloat(numerator1Span[1], FixedPointPrecisionForParameter);
|
||||||
|
float a21 = FixedPointHelper.ToFloat(numerator1Span[2], FixedPointPrecisionForParameter);
|
||||||
|
|
||||||
|
float b11 = FixedPointHelper.ToFloat(denominator1Span[0], FixedPointPrecisionForParameter);
|
||||||
|
float b21 = FixedPointHelper.ToFloat(denominator1Span[1], FixedPointPrecisionForParameter);
|
||||||
|
|
||||||
float mixState = 0f;
|
float mixState = 0f;
|
||||||
|
|
||||||
|
|||||||
@@ -40,11 +40,13 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
SampleRate = serverState.SampleRate;
|
SampleRate = serverState.SampleRate;
|
||||||
Pitch = serverState.Pitch;
|
Pitch = serverState.Pitch;
|
||||||
|
|
||||||
|
Span<Server.Voice.WaveBuffer> waveBufferSpan = serverState.WaveBuffers.AsSpan();
|
||||||
|
|
||||||
WaveBuffers = new WaveBuffer[Constants.VoiceWaveBufferCount];
|
WaveBuffers = new WaveBuffer[Constants.VoiceWaveBufferCount];
|
||||||
|
|
||||||
for (int i = 0; i < WaveBuffers.Length; i++)
|
for (int i = 0; i < WaveBuffers.Length; i++)
|
||||||
{
|
{
|
||||||
ref Server.Voice.WaveBuffer voiceWaveBuffer = ref serverState.WaveBuffers[i];
|
ref Server.Voice.WaveBuffer voiceWaveBuffer = ref waveBufferSpan[i];
|
||||||
|
|
||||||
WaveBuffers[i] = voiceWaveBuffer.ToCommon(1);
|
WaveBuffers[i] = voiceWaveBuffer.ToCommon(1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using Ryujinx.Audio.Renderer.Parameter.Sink;
|
using Ryujinx.Audio.Renderer.Parameter.Sink;
|
||||||
using Ryujinx.Audio.Renderer.Server.MemoryPool;
|
using Ryujinx.Audio.Renderer.Server.MemoryPool;
|
||||||
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
|
||||||
namespace Ryujinx.Audio.Renderer.Dsp.Command
|
namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||||
@@ -29,9 +30,11 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
Input = new ushort[Constants.ChannelCountMax];
|
Input = new ushort[Constants.ChannelCountMax];
|
||||||
InputCount = parameter.InputCount;
|
InputCount = parameter.InputCount;
|
||||||
|
|
||||||
|
Span<byte> inputSpan = parameter.Input.AsSpan();
|
||||||
|
|
||||||
for (int i = 0; i < InputCount; i++)
|
for (int i = 0; i < InputCount; i++)
|
||||||
{
|
{
|
||||||
Input[i] = (ushort)(bufferOffset + parameter.Input[i]);
|
Input[i] = (ushort)(bufferOffset + inputSpan[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
CircularBuffer = circularBufferAddressInfo.GetReference(true);
|
CircularBuffer = circularBufferAddressInfo.GetReference(true);
|
||||||
@@ -59,7 +62,11 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
|
|
||||||
for (int y = 0; y < context.SampleCount; y++)
|
for (int y = 0; y < context.SampleCount; y++)
|
||||||
{
|
{
|
||||||
context.MemoryManager.Write(targetOffset + (ulong)y * TargetChannelCount, PcmHelper.Saturate(inputBuffer[y]));
|
if (inputBuffer != null)
|
||||||
|
{
|
||||||
|
context.MemoryManager.Write(targetOffset + (ulong)y * TargetChannelCount,
|
||||||
|
PcmHelper.Saturate(inputBuffer[y]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
currentOffset += context.SampleCount * TargetChannelCount;
|
currentOffset += context.SampleCount * TargetChannelCount;
|
||||||
|
|||||||
@@ -43,10 +43,13 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
InputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
|
InputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
|
||||||
OutputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
|
OutputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
|
||||||
|
|
||||||
|
Span<byte> inputSpan = _parameter.Input.AsSpan();
|
||||||
|
Span<byte> outputSpan = _parameter.Output.AsSpan();
|
||||||
|
|
||||||
for (int i = 0; i < _parameter.ChannelCount; i++)
|
for (int i = 0; i < _parameter.ChannelCount; i++)
|
||||||
{
|
{
|
||||||
InputBufferIndices[i] = (ushort)(bufferOffset + _parameter.Input[i]);
|
InputBufferIndices[i] = (ushort)(bufferOffset + inputSpan[i]);
|
||||||
OutputBufferIndices[i] = (ushort)(bufferOffset + _parameter.Output[i]);
|
OutputBufferIndices[i] = (ushort)(bufferOffset + outputSpan[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,9 +175,11 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
statistics.MinimumGain = MathF.Min(statistics.MinimumGain, compressionGain * state.OutputGain);
|
statistics.MinimumGain = MathF.Min(statistics.MinimumGain, compressionGain * state.OutputGain);
|
||||||
statistics.MaximumMean = MathF.Max(statistics.MaximumMean, mean);
|
statistics.MaximumMean = MathF.Max(statistics.MaximumMean, mean);
|
||||||
|
|
||||||
|
Span<float> lastSamplesSpan = statistics.LastSamples.AsSpan();
|
||||||
|
|
||||||
for (int channelIndex = 0; channelIndex < _parameter.ChannelCount; channelIndex++)
|
for (int channelIndex = 0; channelIndex < _parameter.ChannelCount; channelIndex++)
|
||||||
{
|
{
|
||||||
statistics.LastSamples[channelIndex] = MathF.Abs(channelInput[channelIndex] * (1f / 32768f));
|
lastSamplesSpan[channelIndex] = MathF.Abs(channelInput[channelIndex] * (1f / 32768f));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,11 +53,13 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
SampleRate = serverState.SampleRate;
|
SampleRate = serverState.SampleRate;
|
||||||
Pitch = serverState.Pitch;
|
Pitch = serverState.Pitch;
|
||||||
|
|
||||||
|
Span<Server.Voice.WaveBuffer> waveBufferSpan = serverState.WaveBuffers.AsSpan();
|
||||||
|
|
||||||
WaveBuffers = new WaveBuffer[Constants.VoiceWaveBufferCount];
|
WaveBuffers = new WaveBuffer[Constants.VoiceWaveBufferCount];
|
||||||
|
|
||||||
for (int i = 0; i < WaveBuffers.Length; i++)
|
for (int i = 0; i < WaveBuffers.Length; i++)
|
||||||
{
|
{
|
||||||
ref Server.Voice.WaveBuffer voiceWaveBuffer = ref serverState.WaveBuffers[i];
|
ref Server.Voice.WaveBuffer voiceWaveBuffer = ref waveBufferSpan[i];
|
||||||
|
|
||||||
WaveBuffers[i] = voiceWaveBuffer.ToCommon(2);
|
WaveBuffers[i] = voiceWaveBuffer.ToCommon(2);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,10 +43,13 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
InputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
|
InputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
|
||||||
OutputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
|
OutputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
|
||||||
|
|
||||||
|
Span<byte> inputSpan = Parameter.Input.AsSpan();
|
||||||
|
Span<byte> outputSpan = Parameter.Output.AsSpan();
|
||||||
|
|
||||||
for (int i = 0; i < Parameter.ChannelCount; i++)
|
for (int i = 0; i < Parameter.ChannelCount; i++)
|
||||||
{
|
{
|
||||||
InputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Input[i]);
|
InputBufferIndices[i] = (ushort)(bufferOffset + inputSpan[i]);
|
||||||
OutputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Output[i]);
|
OutputBufferIndices[i] = (ushort)(bufferOffset + outputSpan[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
DataSourceHelper.RemapLegacyChannelEffectMappingToChannelResourceMapping(newEffectChannelMappingSupported, InputBufferIndices, Parameter.ChannelCount);
|
DataSourceHelper.RemapLegacyChannelEffectMappingToChannelResourceMapping(newEffectChannelMappingSupported, InputBufferIndices, Parameter.ChannelCount);
|
||||||
|
|||||||
@@ -42,14 +42,15 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
ref VoiceUpdateState state = ref State.Span[0];
|
ref VoiceUpdateState state = ref State.Span[0];
|
||||||
|
|
||||||
Span<float> depopBuffer = DepopBuffer.Span;
|
Span<float> depopBuffer = DepopBuffer.Span;
|
||||||
|
Span<float> lastSamplesSpan = state.LastSamples.AsSpan();
|
||||||
|
|
||||||
for (int i = 0; i < MixBufferCount; i++)
|
for (int i = 0; i < MixBufferCount; i++)
|
||||||
{
|
{
|
||||||
if (state.LastSamples[i] != 0)
|
if (lastSamplesSpan[i] != 0)
|
||||||
{
|
{
|
||||||
depopBuffer[OutputBufferIndices[i]] += state.LastSamples[i];
|
depopBuffer[OutputBufferIndices[i]] += lastSamplesSpan[i];
|
||||||
|
|
||||||
state.LastSamples[i] = 0;
|
lastSamplesSpan[i] = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,9 +35,11 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
InputCount = sink.Parameter.InputCount;
|
InputCount = sink.Parameter.InputCount;
|
||||||
InputBufferIndices = new ushort[InputCount];
|
InputBufferIndices = new ushort[InputCount];
|
||||||
|
|
||||||
|
Span<byte> inputSpan = sink.Parameter.Input.AsSpan();
|
||||||
|
|
||||||
for (int i = 0; i < Math.Min(InputCount, Constants.ChannelCountMax); i++)
|
for (int i = 0; i < Math.Min(InputCount, Constants.ChannelCountMax); i++)
|
||||||
{
|
{
|
||||||
InputBufferIndices[i] = (ushort)(bufferOffset + sink.Parameter.Input[i]);
|
InputBufferIndices[i] = (ushort)(bufferOffset + inputSpan[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sink.UpsamplerState != null)
|
if (sink.UpsamplerState != null)
|
||||||
|
|||||||
@@ -38,10 +38,13 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
InputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
|
InputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
|
||||||
OutputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
|
OutputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
|
||||||
|
|
||||||
|
Span<byte> inputSpan = _parameter.Input.AsSpan();
|
||||||
|
Span<byte> outputSpan = _parameter.Output.AsSpan();
|
||||||
|
|
||||||
for (int i = 0; i < _parameter.ChannelCount; i++)
|
for (int i = 0; i < _parameter.ChannelCount; i++)
|
||||||
{
|
{
|
||||||
InputBufferIndices[i] = (ushort)(bufferOffset + _parameter.Input[i]);
|
InputBufferIndices[i] = (ushort)(bufferOffset + inputSpan[i]);
|
||||||
OutputBufferIndices[i] = (ushort)(bufferOffset + _parameter.Output[i]);
|
OutputBufferIndices[i] = (ushort)(bufferOffset + outputSpan[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -49,10 +49,13 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
InputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
|
InputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
|
||||||
OutputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
|
OutputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
|
||||||
|
|
||||||
|
Span<byte> inputSpan = _parameter.Input.AsSpan();
|
||||||
|
Span<byte> outputSpan = _parameter.Output.AsSpan();
|
||||||
|
|
||||||
for (int i = 0; i < _parameter.ChannelCount; i++)
|
for (int i = 0; i < _parameter.ChannelCount; i++)
|
||||||
{
|
{
|
||||||
InputBufferIndices[i] = (ushort)(bufferOffset + _parameter.Input[i]);
|
InputBufferIndices[i] = (ushort)(bufferOffset + inputSpan[i]);
|
||||||
OutputBufferIndices[i] = (ushort)(bufferOffset + _parameter.Output[i]);
|
OutputBufferIndices[i] = (ushort)(bufferOffset + outputSpan[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,8 +153,11 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
{
|
{
|
||||||
ref LimiterStatistics statistics = ref MemoryMarshal.Cast<byte, LimiterStatistics>(ResultState.Span[0].SpecificData)[0];
|
ref LimiterStatistics statistics = ref MemoryMarshal.Cast<byte, LimiterStatistics>(ResultState.Span[0].SpecificData)[0];
|
||||||
|
|
||||||
statistics.InputMax[channelIndex] = Math.Max(statistics.InputMax[channelIndex], sampleInputMax);
|
Span<float> inputMaxSpan = statistics.InputMax.AsSpan();
|
||||||
statistics.CompressionGainMin[channelIndex] = Math.Min(statistics.CompressionGainMin[channelIndex], compressionGain);
|
Span<float> compressionGainMinSpan = statistics.CompressionGainMin.AsSpan();
|
||||||
|
|
||||||
|
inputMaxSpan[channelIndex] = Math.Max(inputMaxSpan[channelIndex], sampleInputMax);
|
||||||
|
compressionGainMinSpan[channelIndex] = Math.Min(compressionGainMinSpan[channelIndex], compressionGain);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,6 +79,10 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
|
|
||||||
public void Process(CommandList context)
|
public void Process(CommandList context)
|
||||||
{
|
{
|
||||||
|
ref VoiceUpdateState state = ref State.Span[0];
|
||||||
|
|
||||||
|
Span<float> lastSamplesSpan = state.LastSamples.AsSpan();
|
||||||
|
|
||||||
for (int i = 0; i < MixBufferCount; i++)
|
for (int i = 0; i < MixBufferCount; i++)
|
||||||
{
|
{
|
||||||
ReadOnlySpan<float> inputBuffer = context.GetBuffer(InputBufferIndices[i]);
|
ReadOnlySpan<float> inputBuffer = context.GetBuffer(InputBufferIndices[i]);
|
||||||
@@ -87,15 +91,13 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
float volume0 = Volume0[i];
|
float volume0 = Volume0[i];
|
||||||
float volume1 = Volume1[i];
|
float volume1 = Volume1[i];
|
||||||
|
|
||||||
ref VoiceUpdateState state = ref State.Span[0];
|
|
||||||
|
|
||||||
if (volume0 != 0 || volume1 != 0)
|
if (volume0 != 0 || volume1 != 0)
|
||||||
{
|
{
|
||||||
state.LastSamples[i] = ProcessMixRampGrouped(outputBuffer, inputBuffer, volume0, volume1, (int)context.SampleCount);
|
lastSamplesSpan[i] = ProcessMixRampGrouped(outputBuffer, inputBuffer, volume0, volume1, (int)context.SampleCount);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
state.LastSamples[i] = 0;
|
lastSamplesSpan[i] = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,9 +43,11 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
|
|
||||||
WaveBuffers = new WaveBuffer[Constants.VoiceWaveBufferCount];
|
WaveBuffers = new WaveBuffer[Constants.VoiceWaveBufferCount];
|
||||||
|
|
||||||
|
Span<Server.Voice.WaveBuffer> waveBufferSpan = serverState.WaveBuffers.AsSpan();
|
||||||
|
|
||||||
for (int i = 0; i < WaveBuffers.Length; i++)
|
for (int i = 0; i < WaveBuffers.Length; i++)
|
||||||
{
|
{
|
||||||
ref Server.Voice.WaveBuffer voiceWaveBuffer = ref serverState.WaveBuffers[i];
|
ref Server.Voice.WaveBuffer voiceWaveBuffer = ref waveBufferSpan[i];
|
||||||
|
|
||||||
WaveBuffers[i] = voiceWaveBuffer.ToCommon(1);
|
WaveBuffers[i] = voiceWaveBuffer.ToCommon(1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,9 +43,11 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
|
|
||||||
WaveBuffers = new WaveBuffer[Constants.VoiceWaveBufferCount];
|
WaveBuffers = new WaveBuffer[Constants.VoiceWaveBufferCount];
|
||||||
|
|
||||||
|
Span<Server.Voice.WaveBuffer> waveBufferSpan = serverState.WaveBuffers.AsSpan();
|
||||||
|
|
||||||
for (int i = 0; i < WaveBuffers.Length; i++)
|
for (int i = 0; i < WaveBuffers.Length; i++)
|
||||||
{
|
{
|
||||||
ref Server.Voice.WaveBuffer voiceWaveBuffer = ref serverState.WaveBuffers[i];
|
ref Server.Voice.WaveBuffer voiceWaveBuffer = ref waveBufferSpan[i];
|
||||||
|
|
||||||
WaveBuffers[i] = voiceWaveBuffer.ToCommon(1);
|
WaveBuffers[i] = voiceWaveBuffer.ToCommon(1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,10 +66,13 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
InputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
|
InputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
|
||||||
OutputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
|
OutputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
|
||||||
|
|
||||||
|
Span<byte> inputSpan = Parameter.Input.AsSpan();
|
||||||
|
Span<byte> outputSpan = Parameter.Output.AsSpan();
|
||||||
|
|
||||||
for (int i = 0; i < Parameter.ChannelCount; i++)
|
for (int i = 0; i < Parameter.ChannelCount; i++)
|
||||||
{
|
{
|
||||||
InputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Input[i]);
|
InputBufferIndices[i] = (ushort)(bufferOffset + inputSpan[i]);
|
||||||
OutputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Output[i]);
|
OutputBufferIndices[i] = (ushort)(bufferOffset + outputSpan[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: We do the opposite as Nintendo here for now to restore previous behaviour
|
// NOTE: We do the opposite as Nintendo here for now to restore previous behaviour
|
||||||
|
|||||||
@@ -64,10 +64,13 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
InputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
|
InputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
|
||||||
OutputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
|
OutputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
|
||||||
|
|
||||||
|
Span<byte> inputSpan = Parameter.Input.AsSpan();
|
||||||
|
Span<byte> outputSpan = Parameter.Output.AsSpan();
|
||||||
|
|
||||||
for (int i = 0; i < Parameter.ChannelCount; i++)
|
for (int i = 0; i < Parameter.ChannelCount; i++)
|
||||||
{
|
{
|
||||||
InputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Input[i]);
|
InputBufferIndices[i] = (ushort)(bufferOffset + inputSpan[i]);
|
||||||
OutputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Output[i]);
|
OutputBufferIndices[i] = (ushort)(bufferOffset + outputSpan[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
IsLongSizePreDelaySupported = isLongSizePreDelaySupported;
|
IsLongSizePreDelaySupported = isLongSizePreDelaySupported;
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ namespace Ryujinx.Audio.Renderer.Dsp
|
|||||||
{
|
{
|
||||||
int tempBufferIndex = 0;
|
int tempBufferIndex = 0;
|
||||||
|
|
||||||
if (!info.DecodingBehaviour.HasFlag(DecodingBehaviour.SkipPitchAndSampleRateConversion))
|
if ((info.DecodingBehaviour & DecodingBehaviour.SkipPitchAndSampleRateConversion) != DecodingBehaviour.SkipPitchAndSampleRateConversion)
|
||||||
{
|
{
|
||||||
voiceState.Pitch.AsSpan()[..pitchMaxLength].CopyTo(tempBuffer);
|
voiceState.Pitch.AsSpan()[..pitchMaxLength].CopyTo(tempBuffer);
|
||||||
tempBufferIndex += pitchMaxLength;
|
tempBufferIndex += pitchMaxLength;
|
||||||
@@ -208,7 +208,7 @@ namespace Ryujinx.Audio.Renderer.Dsp
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (info.DecodingBehaviour.HasFlag(DecodingBehaviour.PlayedSampleCountResetWhenLooping))
|
if ((info.DecodingBehaviour & DecodingBehaviour.PlayedSampleCountResetWhenLooping) == DecodingBehaviour.PlayedSampleCountResetWhenLooping)
|
||||||
{
|
{
|
||||||
playedSampleCount = 0;
|
playedSampleCount = 0;
|
||||||
}
|
}
|
||||||
@@ -222,7 +222,7 @@ namespace Ryujinx.Audio.Renderer.Dsp
|
|||||||
|
|
||||||
Span<int> outputSpanInt = MemoryMarshal.Cast<float, int>(outputBuffer[i..]);
|
Span<int> outputSpanInt = MemoryMarshal.Cast<float, int>(outputBuffer[i..]);
|
||||||
|
|
||||||
if (info.DecodingBehaviour.HasFlag(DecodingBehaviour.SkipPitchAndSampleRateConversion))
|
if ((info.DecodingBehaviour & DecodingBehaviour.SkipPitchAndSampleRateConversion) == DecodingBehaviour.SkipPitchAndSampleRateConversion)
|
||||||
{
|
{
|
||||||
for (int j = 0; j < y; j++)
|
for (int j = 0; j < y; j++)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.State
|
|||||||
|
|
||||||
DelayFeedbackBaseGain = (1.0f - channelSpread) * FeedbackGain;
|
DelayFeedbackBaseGain = (1.0f - channelSpread) * FeedbackGain;
|
||||||
|
|
||||||
if (parameter.ChannelCount == 4 || parameter.ChannelCount == 6)
|
if (parameter.ChannelCount is 4 or 6)
|
||||||
{
|
{
|
||||||
DelayFeedbackCrossGain = channelSpread * 0.5f * FeedbackGain;
|
DelayFeedbackCrossGain = channelSpread * 0.5f * FeedbackGain;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,11 +40,12 @@ namespace Ryujinx.Audio.Renderer.Dsp
|
|||||||
}
|
}
|
||||||
|
|
||||||
Array20<float> result = new();
|
Array20<float> result = new();
|
||||||
|
Span<float> resultSpan = result.AsSpan();
|
||||||
|
|
||||||
for (int i = 0; i < FilterBankLength; i++)
|
for (int i = 0; i < FilterBankLength; i++)
|
||||||
{
|
{
|
||||||
float x = (Bank0CenterIndex - i) + offset;
|
float x = (Bank0CenterIndex - i) + offset;
|
||||||
result[i] = Sinc(x) * BlackmanWindow(x / FilterBankLength + 0.5f);
|
resultSpan[i] = Sinc(x) * BlackmanWindow(x / FilterBankLength + 0.5f);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@@ -78,6 +79,9 @@ namespace Ryujinx.Audio.Renderer.Dsp
|
|||||||
Debug.Assert(state.History.Length == HistoryLength);
|
Debug.Assert(state.History.Length == HistoryLength);
|
||||||
Debug.Assert(bank.Length == FilterBankLength);
|
Debug.Assert(bank.Length == FilterBankLength);
|
||||||
|
|
||||||
|
Span<float> bankSpan = bank.AsSpan();
|
||||||
|
Span<float> historySpan = state.History.AsSpan();
|
||||||
|
|
||||||
int curIdx = 0;
|
int curIdx = 0;
|
||||||
if (Vector.IsHardwareAccelerated)
|
if (Vector.IsHardwareAccelerated)
|
||||||
{
|
{
|
||||||
@@ -87,15 +91,15 @@ namespace Ryujinx.Audio.Renderer.Dsp
|
|||||||
while (curIdx < stopIdx)
|
while (curIdx < stopIdx)
|
||||||
{
|
{
|
||||||
result += Vector.Dot(
|
result += Vector.Dot(
|
||||||
new Vector<float>(bank.AsSpan().Slice(curIdx, Vector<float>.Count)),
|
new Vector<float>(bankSpan[curIdx..(curIdx + Vector<float>.Count)]),
|
||||||
new Vector<float>(state.History.AsSpan().Slice(curIdx, Vector<float>.Count)));
|
new Vector<float>(historySpan[curIdx..(curIdx + Vector<float>.Count)]));
|
||||||
curIdx += Vector<float>.Count;
|
curIdx += Vector<float>.Count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
while (curIdx < FilterBankLength)
|
while (curIdx < FilterBankLength)
|
||||||
{
|
{
|
||||||
result += bank[curIdx] * state.History[curIdx];
|
result += bankSpan[curIdx] * historySpan[curIdx];
|
||||||
curIdx++;
|
curIdx++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ namespace Ryujinx.Audio.Renderer.Parameter
|
|||||||
/// <returns>Returns true if the channel count is valid.</returns>
|
/// <returns>Returns true if the channel count is valid.</returns>
|
||||||
public static bool IsChannelCountValid(int channelCount)
|
public static bool IsChannelCountValid(int channelCount)
|
||||||
{
|
{
|
||||||
return channelCount == 1 || channelCount == 2 || channelCount == 4 || channelCount == 6;
|
return channelCount is 1 or 2 or 4 or 6;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ namespace Ryujinx.Audio.Renderer.Parameter
|
|||||||
/// <returns>Returns true if the channel count is valid.</returns>
|
/// <returns>Returns true if the channel count is valid.</returns>
|
||||||
public static bool IsChannelCountValid(int channelCount)
|
public static bool IsChannelCountValid(int channelCount)
|
||||||
{
|
{
|
||||||
return channelCount == 1 || channelCount == 2 || channelCount == 4 || channelCount == 6;
|
return channelCount is 1 or 2 or 4 or 6;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -436,7 +436,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
PoolMapper poolMapper = new PoolMapper(_processHandle, _memoryPools, _behaviourContext.IsMemoryPoolForceMappingEnabled());
|
PoolMapper poolMapper = new(_processHandle, _memoryPools, _behaviourContext.IsMemoryPoolForceMappingEnabled());
|
||||||
|
|
||||||
result = stateUpdater.UpdateVoices(_voiceContext, poolMapper);
|
result = stateUpdater.UpdateVoices(_voiceContext, poolMapper);
|
||||||
|
|
||||||
@@ -532,13 +532,13 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
|
|
||||||
CommandType commandType = command.CommandType;
|
CommandType commandType = command.CommandType;
|
||||||
|
|
||||||
if (commandType == CommandType.AdpcmDataSourceVersion1 ||
|
if (commandType is CommandType.AdpcmDataSourceVersion1
|
||||||
commandType == CommandType.AdpcmDataSourceVersion2 ||
|
or CommandType.AdpcmDataSourceVersion2
|
||||||
commandType == CommandType.PcmInt16DataSourceVersion1 ||
|
or CommandType.PcmInt16DataSourceVersion1
|
||||||
commandType == CommandType.PcmInt16DataSourceVersion2 ||
|
or CommandType.PcmInt16DataSourceVersion2
|
||||||
commandType == CommandType.PcmFloatDataSourceVersion1 ||
|
or CommandType.PcmFloatDataSourceVersion1
|
||||||
commandType == CommandType.PcmFloatDataSourceVersion2 ||
|
or CommandType.PcmFloatDataSourceVersion2
|
||||||
commandType == CommandType.Performance)
|
or CommandType.Performance)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -141,18 +141,20 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
{
|
{
|
||||||
bool supportsOptimizedPath = _rendererContext.BehaviourContext.UseMultiTapBiquadFilterProcessing();
|
bool supportsOptimizedPath = _rendererContext.BehaviourContext.UseMultiTapBiquadFilterProcessing();
|
||||||
|
|
||||||
if (supportsOptimizedPath && voiceState.BiquadFilters[0].Enable && voiceState.BiquadFilters[1].Enable)
|
Span<BiquadFilterParameter> biquadFiltersSpan = voiceState.BiquadFilters.AsSpan();
|
||||||
|
|
||||||
|
if (supportsOptimizedPath && biquadFiltersSpan[0].Enable && biquadFiltersSpan[1].Enable)
|
||||||
{
|
{
|
||||||
Memory<byte> biquadStateRawMemory = SpanMemoryManager<byte>.Cast(state)[..(Unsafe.SizeOf<BiquadFilterState>() * Constants.VoiceBiquadFilterCount)];
|
Memory<byte> biquadStateRawMemory = SpanMemoryManager<byte>.Cast(state)[..(Unsafe.SizeOf<BiquadFilterState>() * Constants.VoiceBiquadFilterCount)];
|
||||||
Memory<BiquadFilterState> stateMemory = SpanMemoryManager<BiquadFilterState>.Cast(biquadStateRawMemory);
|
Memory<BiquadFilterState> stateMemory = SpanMemoryManager<BiquadFilterState>.Cast(biquadStateRawMemory);
|
||||||
|
|
||||||
_commandBuffer.GenerateMultiTapBiquadFilter(baseIndex, voiceState.BiquadFilters.AsSpan(), stateMemory, bufferOffset, bufferOffset, voiceState.BiquadFilterNeedInitialization, nodeId);
|
_commandBuffer.GenerateMultiTapBiquadFilter(baseIndex, biquadFiltersSpan, stateMemory, bufferOffset, bufferOffset, voiceState.BiquadFilterNeedInitialization, nodeId);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (int i = 0; i < voiceState.BiquadFilters.Length; i++)
|
for (int i = 0; i < biquadFiltersSpan.Length; i++)
|
||||||
{
|
{
|
||||||
ref BiquadFilterParameter filter = ref voiceState.BiquadFilters[i];
|
ref BiquadFilterParameter filter = ref biquadFiltersSpan[i];
|
||||||
|
|
||||||
if (filter.Enable)
|
if (filter.Enable)
|
||||||
{
|
{
|
||||||
@@ -312,11 +314,14 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
int nodeId = voiceState.NodeId;
|
int nodeId = voiceState.NodeId;
|
||||||
uint channelsCount = voiceState.ChannelsCount;
|
uint channelsCount = voiceState.ChannelsCount;
|
||||||
|
|
||||||
|
Span<int> channelResourceIdsSpan = voiceState.ChannelResourceIds.AsSpan();
|
||||||
|
Span<BiquadFilterParameter> biquadFiltersSpan = voiceState.BiquadFilters.AsSpan();
|
||||||
|
|
||||||
for (int channelIndex = 0; channelIndex < channelsCount; channelIndex++)
|
for (int channelIndex = 0; channelIndex < channelsCount; channelIndex++)
|
||||||
{
|
{
|
||||||
Memory<VoiceUpdateState> dspStateMemory = _voiceContext.GetUpdateStateForDsp(voiceState.ChannelResourceIds[channelIndex]);
|
Memory<VoiceUpdateState> dspStateMemory = _voiceContext.GetUpdateStateForDsp(channelResourceIdsSpan[channelIndex]);
|
||||||
|
|
||||||
ref VoiceChannelResource channelResource = ref _voiceContext.GetChannelResource(voiceState.ChannelResourceIds[channelIndex]);
|
ref VoiceChannelResource channelResource = ref _voiceContext.GetChannelResource(channelResourceIdsSpan[channelIndex]);
|
||||||
|
|
||||||
PerformanceDetailType dataSourceDetailType = PerformanceDetailType.Adpcm;
|
PerformanceDetailType dataSourceDetailType = PerformanceDetailType.Adpcm;
|
||||||
|
|
||||||
@@ -476,7 +481,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
|
|
||||||
for (int i = 0; i < voiceState.BiquadFilterNeedInitialization.Length; i++)
|
for (int i = 0; i < voiceState.BiquadFilterNeedInitialization.Length; i++)
|
||||||
{
|
{
|
||||||
voiceState.BiquadFilterNeedInitialization[i] = voiceState.BiquadFilters[i].Enable;
|
voiceState.BiquadFilterNeedInitialization[i] = biquadFiltersSpan[i].Enable;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -526,15 +531,19 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
|
|
||||||
if (effect.IsEnabled)
|
if (effect.IsEnabled)
|
||||||
{
|
{
|
||||||
|
Span<float> volumesSpan = effect.Parameter.Volumes.AsSpan();
|
||||||
|
Span<byte> inputSpan = effect.Parameter.Input.AsSpan();
|
||||||
|
Span<byte> outputSpan = effect.Parameter.Output.AsSpan();
|
||||||
|
|
||||||
for (int i = 0; i < effect.Parameter.MixesCount; i++)
|
for (int i = 0; i < effect.Parameter.MixesCount; i++)
|
||||||
{
|
{
|
||||||
if (effect.Parameter.Volumes[i] != 0.0f)
|
if (volumesSpan[i] != 0.0f)
|
||||||
{
|
{
|
||||||
_commandBuffer.GenerateMix(
|
_commandBuffer.GenerateMix(
|
||||||
(uint)bufferOffset + effect.Parameter.Input[i],
|
(uint)bufferOffset + inputSpan[i],
|
||||||
(uint)bufferOffset + effect.Parameter.Output[i],
|
(uint)bufferOffset + outputSpan[i],
|
||||||
nodeId,
|
nodeId,
|
||||||
effect.Parameter.Volumes[i]);
|
volumesSpan[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -554,6 +563,10 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
{
|
{
|
||||||
int i = 0;
|
int i = 0;
|
||||||
uint writeOffset = 0;
|
uint writeOffset = 0;
|
||||||
|
|
||||||
|
Span<byte> inputSpan = effect.Parameter.Input.AsSpan();
|
||||||
|
Span<byte> outputSpan = effect.Parameter.Output.AsSpan();
|
||||||
|
|
||||||
for (uint channelIndex = effect.Parameter.ChannelCount; channelIndex != 0; channelIndex--)
|
for (uint channelIndex = effect.Parameter.ChannelCount; channelIndex != 0; channelIndex--)
|
||||||
{
|
{
|
||||||
uint newUpdateCount = writeOffset + _commandBuffer.CommandList.SampleCount;
|
uint newUpdateCount = writeOffset + _commandBuffer.CommandList.SampleCount;
|
||||||
@@ -571,8 +584,8 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
|
|
||||||
_commandBuffer.GenerateAuxEffect(
|
_commandBuffer.GenerateAuxEffect(
|
||||||
bufferOffset,
|
bufferOffset,
|
||||||
effect.Parameter.Input[i],
|
inputSpan[i],
|
||||||
effect.Parameter.Output[i],
|
outputSpan[i],
|
||||||
ref effect.State,
|
ref effect.State,
|
||||||
effect.IsEnabled,
|
effect.IsEnabled,
|
||||||
effect.Parameter.BufferStorageSize,
|
effect.Parameter.BufferStorageSize,
|
||||||
@@ -620,6 +633,9 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
{
|
{
|
||||||
Debug.Assert(effect.Type == EffectType.BiquadFilter);
|
Debug.Assert(effect.Type == EffectType.BiquadFilter);
|
||||||
|
|
||||||
|
Span<byte> inputSpan = effect.Parameter.Input.AsSpan();
|
||||||
|
Span<byte> outputSpan = effect.Parameter.Output.AsSpan();
|
||||||
|
|
||||||
if (effect.IsEnabled)
|
if (effect.IsEnabled)
|
||||||
{
|
{
|
||||||
bool needInitialization = effect.Parameter.Status == UsageState.Invalid ||
|
bool needInitialization = effect.Parameter.Status == UsageState.Invalid ||
|
||||||
@@ -639,8 +655,8 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
(int)bufferOffset,
|
(int)bufferOffset,
|
||||||
ref parameter,
|
ref parameter,
|
||||||
effect.State.Slice(i, 1),
|
effect.State.Slice(i, 1),
|
||||||
effect.Parameter.Input[i],
|
inputSpan[i],
|
||||||
effect.Parameter.Output[i],
|
outputSpan[i],
|
||||||
needInitialization,
|
needInitialization,
|
||||||
nodeId);
|
nodeId);
|
||||||
}
|
}
|
||||||
@@ -649,8 +665,8 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
{
|
{
|
||||||
for (int i = 0; i < effect.Parameter.ChannelCount; i++)
|
for (int i = 0; i < effect.Parameter.ChannelCount; i++)
|
||||||
{
|
{
|
||||||
uint inputBufferIndex = bufferOffset + effect.Parameter.Input[i];
|
uint inputBufferIndex = bufferOffset + inputSpan[i];
|
||||||
uint outputBufferIndex = bufferOffset + effect.Parameter.Output[i];
|
uint outputBufferIndex = bufferOffset + outputSpan[i];
|
||||||
|
|
||||||
// If the input and output isn't the same, generate a command.
|
// If the input and output isn't the same, generate a command.
|
||||||
if (inputBufferIndex != outputBufferIndex)
|
if (inputBufferIndex != outputBufferIndex)
|
||||||
@@ -702,6 +718,8 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
int i = 0;
|
int i = 0;
|
||||||
uint writeOffset = 0;
|
uint writeOffset = 0;
|
||||||
|
|
||||||
|
Span<byte> inputSpan = effect.Parameter.Input.AsSpan();
|
||||||
|
|
||||||
for (uint channelIndex = effect.Parameter.ChannelCount; channelIndex != 0; channelIndex--)
|
for (uint channelIndex = effect.Parameter.ChannelCount; channelIndex != 0; channelIndex--)
|
||||||
{
|
{
|
||||||
uint newUpdateCount = writeOffset + _commandBuffer.CommandList.SampleCount;
|
uint newUpdateCount = writeOffset + _commandBuffer.CommandList.SampleCount;
|
||||||
@@ -719,7 +737,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
|
|
||||||
_commandBuffer.GenerateCaptureEffect(
|
_commandBuffer.GenerateCaptureEffect(
|
||||||
bufferOffset,
|
bufferOffset,
|
||||||
effect.Parameter.Input[i],
|
inputSpan[i],
|
||||||
effect.State.SendBufferInfo,
|
effect.State.SendBufferInfo,
|
||||||
effect.IsEnabled,
|
effect.IsEnabled,
|
||||||
effect.Parameter.BufferStorageSize,
|
effect.Parameter.BufferStorageSize,
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
|
|
||||||
public uint Estimate(PerformanceCommand command)
|
public uint Estimate(PerformanceCommand command)
|
||||||
{
|
{
|
||||||
Debug.Assert(_sampleCount == 160 || _sampleCount == 240);
|
Debug.Assert(_sampleCount is 160 or 240);
|
||||||
|
|
||||||
if (_sampleCount == 160)
|
if (_sampleCount == 160)
|
||||||
{
|
{
|
||||||
@@ -32,7 +32,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
|
|
||||||
public uint Estimate(ClearMixBufferCommand command)
|
public uint Estimate(ClearMixBufferCommand command)
|
||||||
{
|
{
|
||||||
Debug.Assert(_sampleCount == 160 || _sampleCount == 240);
|
Debug.Assert(_sampleCount is 160 or 240);
|
||||||
|
|
||||||
float costPerBuffer = 668.8f;
|
float costPerBuffer = 668.8f;
|
||||||
float baseCost = 193.2f;
|
float baseCost = 193.2f;
|
||||||
@@ -48,7 +48,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
|
|
||||||
public uint Estimate(BiquadFilterCommand command)
|
public uint Estimate(BiquadFilterCommand command)
|
||||||
{
|
{
|
||||||
Debug.Assert(_sampleCount == 160 || _sampleCount == 240);
|
Debug.Assert(_sampleCount is 160 or 240);
|
||||||
|
|
||||||
if (_sampleCount == 160)
|
if (_sampleCount == 160)
|
||||||
{
|
{
|
||||||
@@ -62,7 +62,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
{
|
{
|
||||||
const float CostPerSample = 7.245f;
|
const float CostPerSample = 7.245f;
|
||||||
|
|
||||||
Debug.Assert(_sampleCount == 160 || _sampleCount == 240);
|
Debug.Assert(_sampleCount is 160 or 240);
|
||||||
|
|
||||||
int volumeCount = 0;
|
int volumeCount = 0;
|
||||||
|
|
||||||
@@ -79,7 +79,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
|
|
||||||
public uint Estimate(MixRampCommand command)
|
public uint Estimate(MixRampCommand command)
|
||||||
{
|
{
|
||||||
Debug.Assert(_sampleCount == 160 || _sampleCount == 240);
|
Debug.Assert(_sampleCount is 160 or 240);
|
||||||
|
|
||||||
if (_sampleCount == 160)
|
if (_sampleCount == 160)
|
||||||
{
|
{
|
||||||
@@ -91,7 +91,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
|
|
||||||
public uint Estimate(DepopPrepareCommand command)
|
public uint Estimate(DepopPrepareCommand command)
|
||||||
{
|
{
|
||||||
Debug.Assert(_sampleCount == 160 || _sampleCount == 240);
|
Debug.Assert(_sampleCount is 160 or 240);
|
||||||
|
|
||||||
if (_sampleCount == 160)
|
if (_sampleCount == 160)
|
||||||
{
|
{
|
||||||
@@ -103,7 +103,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
|
|
||||||
public uint Estimate(VolumeRampCommand command)
|
public uint Estimate(VolumeRampCommand command)
|
||||||
{
|
{
|
||||||
Debug.Assert(_sampleCount == 160 || _sampleCount == 240);
|
Debug.Assert(_sampleCount is 160 or 240);
|
||||||
|
|
||||||
if (_sampleCount == 160)
|
if (_sampleCount == 160)
|
||||||
{
|
{
|
||||||
@@ -115,7 +115,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
|
|
||||||
public uint Estimate(PcmInt16DataSourceCommandVersion1 command)
|
public uint Estimate(PcmInt16DataSourceCommandVersion1 command)
|
||||||
{
|
{
|
||||||
Debug.Assert(_sampleCount == 160 || _sampleCount == 240);
|
Debug.Assert(_sampleCount is 160 or 240);
|
||||||
|
|
||||||
float costPerSample = 1195.5f;
|
float costPerSample = 1195.5f;
|
||||||
float baseCost = 7797.0f;
|
float baseCost = 7797.0f;
|
||||||
@@ -131,7 +131,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
|
|
||||||
public uint Estimate(AdpcmDataSourceCommandVersion1 command)
|
public uint Estimate(AdpcmDataSourceCommandVersion1 command)
|
||||||
{
|
{
|
||||||
Debug.Assert(_sampleCount == 160 || _sampleCount == 240);
|
Debug.Assert(_sampleCount is 160 or 240);
|
||||||
|
|
||||||
float costPerSample = 3564.1f;
|
float costPerSample = 3564.1f;
|
||||||
float baseCost = 6225.5f;
|
float baseCost = 6225.5f;
|
||||||
@@ -147,7 +147,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
|
|
||||||
public uint Estimate(DepopForMixBuffersCommand command)
|
public uint Estimate(DepopForMixBuffersCommand command)
|
||||||
{
|
{
|
||||||
Debug.Assert(_sampleCount == 160 || _sampleCount == 240);
|
Debug.Assert(_sampleCount is 160 or 240);
|
||||||
|
|
||||||
if (_sampleCount == 160)
|
if (_sampleCount == 160)
|
||||||
{
|
{
|
||||||
@@ -159,7 +159,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
|
|
||||||
public uint Estimate(CopyMixBufferCommand command)
|
public uint Estimate(CopyMixBufferCommand command)
|
||||||
{
|
{
|
||||||
Debug.Assert(_sampleCount == 160 || _sampleCount == 240);
|
Debug.Assert(_sampleCount is 160 or 240);
|
||||||
|
|
||||||
if (_sampleCount == 160)
|
if (_sampleCount == 160)
|
||||||
{
|
{
|
||||||
@@ -171,7 +171,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
|
|
||||||
public uint Estimate(MixCommand command)
|
public uint Estimate(MixCommand command)
|
||||||
{
|
{
|
||||||
Debug.Assert(_sampleCount == 160 || _sampleCount == 240);
|
Debug.Assert(_sampleCount is 160 or 240);
|
||||||
|
|
||||||
if (_sampleCount == 160)
|
if (_sampleCount == 160)
|
||||||
{
|
{
|
||||||
@@ -183,7 +183,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
|
|
||||||
public uint Estimate(DelayCommand command)
|
public uint Estimate(DelayCommand command)
|
||||||
{
|
{
|
||||||
Debug.Assert(_sampleCount == 160 || _sampleCount == 240);
|
Debug.Assert(_sampleCount is 160 or 240);
|
||||||
|
|
||||||
if (_sampleCount == 160)
|
if (_sampleCount == 160)
|
||||||
{
|
{
|
||||||
@@ -234,7 +234,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
|
|
||||||
public uint Estimate(ReverbCommand command)
|
public uint Estimate(ReverbCommand command)
|
||||||
{
|
{
|
||||||
Debug.Assert(_sampleCount == 160 || _sampleCount == 240);
|
Debug.Assert(_sampleCount is 160 or 240);
|
||||||
|
|
||||||
if (_sampleCount == 160)
|
if (_sampleCount == 160)
|
||||||
{
|
{
|
||||||
@@ -285,7 +285,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
|
|
||||||
public uint Estimate(Reverb3dCommand command)
|
public uint Estimate(Reverb3dCommand command)
|
||||||
{
|
{
|
||||||
Debug.Assert(_sampleCount == 160 || _sampleCount == 240);
|
Debug.Assert(_sampleCount is 160 or 240);
|
||||||
|
|
||||||
if (_sampleCount == 160)
|
if (_sampleCount == 160)
|
||||||
{
|
{
|
||||||
@@ -335,7 +335,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
|
|
||||||
public uint Estimate(AuxiliaryBufferCommand command)
|
public uint Estimate(AuxiliaryBufferCommand command)
|
||||||
{
|
{
|
||||||
Debug.Assert(_sampleCount == 160 || _sampleCount == 240);
|
Debug.Assert(_sampleCount is 160 or 240);
|
||||||
|
|
||||||
if (_sampleCount == 160)
|
if (_sampleCount == 160)
|
||||||
{
|
{
|
||||||
@@ -357,7 +357,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
|
|
||||||
public uint Estimate(VolumeCommand command)
|
public uint Estimate(VolumeCommand command)
|
||||||
{
|
{
|
||||||
Debug.Assert(_sampleCount == 160 || _sampleCount == 240);
|
Debug.Assert(_sampleCount is 160 or 240);
|
||||||
|
|
||||||
if (_sampleCount == 160)
|
if (_sampleCount == 160)
|
||||||
{
|
{
|
||||||
@@ -369,7 +369,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
|
|
||||||
public uint Estimate(CircularBufferSinkCommand command)
|
public uint Estimate(CircularBufferSinkCommand command)
|
||||||
{
|
{
|
||||||
Debug.Assert(_sampleCount == 160 || _sampleCount == 240);
|
Debug.Assert(_sampleCount is 160 or 240);
|
||||||
|
|
||||||
float costPerBuffer = 1726.0f;
|
float costPerBuffer = 1726.0f;
|
||||||
float baseCost = 1369.7f;
|
float baseCost = 1369.7f;
|
||||||
@@ -385,7 +385,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
|
|
||||||
public uint Estimate(DownMixSurroundToStereoCommand command)
|
public uint Estimate(DownMixSurroundToStereoCommand command)
|
||||||
{
|
{
|
||||||
Debug.Assert(_sampleCount == 160 || _sampleCount == 240);
|
Debug.Assert(_sampleCount is 160 or 240);
|
||||||
|
|
||||||
if (_sampleCount == 160)
|
if (_sampleCount == 160)
|
||||||
{
|
{
|
||||||
@@ -397,7 +397,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
|
|
||||||
public uint Estimate(UpsampleCommand command)
|
public uint Estimate(UpsampleCommand command)
|
||||||
{
|
{
|
||||||
Debug.Assert(_sampleCount == 160 || _sampleCount == 240);
|
Debug.Assert(_sampleCount is 160 or 240);
|
||||||
|
|
||||||
if (_sampleCount == 160)
|
if (_sampleCount == 160)
|
||||||
{
|
{
|
||||||
@@ -409,8 +409,8 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
|
|
||||||
public uint Estimate(DeviceSinkCommand command)
|
public uint Estimate(DeviceSinkCommand command)
|
||||||
{
|
{
|
||||||
Debug.Assert(_sampleCount == 160 || _sampleCount == 240);
|
Debug.Assert(_sampleCount is 160 or 240);
|
||||||
Debug.Assert(command.InputCount == 2 || command.InputCount == 6);
|
Debug.Assert(command.InputCount is 2 or 6);
|
||||||
|
|
||||||
if (command.InputCount == 2)
|
if (command.InputCount == 2)
|
||||||
{
|
{
|
||||||
@@ -433,7 +433,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
public uint Estimate(PcmFloatDataSourceCommandVersion1 command)
|
public uint Estimate(PcmFloatDataSourceCommandVersion1 command)
|
||||||
{
|
{
|
||||||
// NOTE: This was added between REV7 and REV8 and for some reasons the estimator v2 was changed...
|
// NOTE: This was added between REV7 and REV8 and for some reasons the estimator v2 was changed...
|
||||||
Debug.Assert(_sampleCount == 160 || _sampleCount == 240);
|
Debug.Assert(_sampleCount is 160 or 240);
|
||||||
|
|
||||||
float costPerSample = 3490.9f;
|
float costPerSample = 3490.9f;
|
||||||
float baseCost = 10091.0f;
|
float baseCost = 10091.0f;
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
|
|
||||||
public uint Estimate(PerformanceCommand command)
|
public uint Estimate(PerformanceCommand command)
|
||||||
{
|
{
|
||||||
Debug.Assert(SampleCount == 160 || SampleCount == 240);
|
Debug.Assert(SampleCount is 160 or 240);
|
||||||
|
|
||||||
if (SampleCount == 160)
|
if (SampleCount == 160)
|
||||||
{
|
{
|
||||||
@@ -35,7 +35,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
|
|
||||||
public uint Estimate(ClearMixBufferCommand command)
|
public uint Estimate(ClearMixBufferCommand command)
|
||||||
{
|
{
|
||||||
Debug.Assert(SampleCount == 160 || SampleCount == 240);
|
Debug.Assert(SampleCount is 160 or 240);
|
||||||
|
|
||||||
float costPerBuffer = 440.68f;
|
float costPerBuffer = 440.68f;
|
||||||
float baseCost = 0;
|
float baseCost = 0;
|
||||||
@@ -50,7 +50,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
|
|
||||||
public uint Estimate(BiquadFilterCommand command)
|
public uint Estimate(BiquadFilterCommand command)
|
||||||
{
|
{
|
||||||
Debug.Assert(SampleCount == 160 || SampleCount == 240);
|
Debug.Assert(SampleCount is 160 or 240);
|
||||||
|
|
||||||
if (SampleCount == 160)
|
if (SampleCount == 160)
|
||||||
{
|
{
|
||||||
@@ -64,7 +64,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
{
|
{
|
||||||
float costPerSample = 6.4434f;
|
float costPerSample = 6.4434f;
|
||||||
|
|
||||||
Debug.Assert(SampleCount == 160 || SampleCount == 240);
|
Debug.Assert(SampleCount is 160 or 240);
|
||||||
|
|
||||||
if (SampleCount == 160)
|
if (SampleCount == 160)
|
||||||
{
|
{
|
||||||
@@ -86,7 +86,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
|
|
||||||
public uint Estimate(MixRampCommand command)
|
public uint Estimate(MixRampCommand command)
|
||||||
{
|
{
|
||||||
Debug.Assert(SampleCount == 160 || SampleCount == 240);
|
Debug.Assert(SampleCount is 160 or 240);
|
||||||
|
|
||||||
if (SampleCount == 160)
|
if (SampleCount == 160)
|
||||||
{
|
{
|
||||||
@@ -103,7 +103,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
|
|
||||||
public uint Estimate(VolumeRampCommand command)
|
public uint Estimate(VolumeRampCommand command)
|
||||||
{
|
{
|
||||||
Debug.Assert(SampleCount == 160 || SampleCount == 240);
|
Debug.Assert(SampleCount is 160 or 240);
|
||||||
|
|
||||||
if (SampleCount == 160)
|
if (SampleCount == 160)
|
||||||
{
|
{
|
||||||
@@ -115,7 +115,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
|
|
||||||
public uint Estimate(PcmInt16DataSourceCommandVersion1 command)
|
public uint Estimate(PcmInt16DataSourceCommandVersion1 command)
|
||||||
{
|
{
|
||||||
Debug.Assert(SampleCount == 160 || SampleCount == 240);
|
Debug.Assert(SampleCount is 160 or 240);
|
||||||
|
|
||||||
float costPerSample = 710.143f;
|
float costPerSample = 710.143f;
|
||||||
float baseCost = 7853.286f;
|
float baseCost = 7853.286f;
|
||||||
@@ -131,7 +131,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
|
|
||||||
public uint Estimate(AdpcmDataSourceCommandVersion1 command)
|
public uint Estimate(AdpcmDataSourceCommandVersion1 command)
|
||||||
{
|
{
|
||||||
Debug.Assert(SampleCount == 160 || SampleCount == 240);
|
Debug.Assert(SampleCount is 160 or 240);
|
||||||
|
|
||||||
float costPerSample = 3564.1f;
|
float costPerSample = 3564.1f;
|
||||||
float baseCost = 9736.702f;
|
float baseCost = 9736.702f;
|
||||||
@@ -147,7 +147,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
|
|
||||||
public uint Estimate(DepopForMixBuffersCommand command)
|
public uint Estimate(DepopForMixBuffersCommand command)
|
||||||
{
|
{
|
||||||
Debug.Assert(SampleCount == 160 || SampleCount == 240);
|
Debug.Assert(SampleCount is 160 or 240);
|
||||||
|
|
||||||
if (SampleCount == 160)
|
if (SampleCount == 160)
|
||||||
{
|
{
|
||||||
@@ -159,7 +159,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
|
|
||||||
public uint Estimate(CopyMixBufferCommand command)
|
public uint Estimate(CopyMixBufferCommand command)
|
||||||
{
|
{
|
||||||
Debug.Assert(SampleCount == 160 || SampleCount == 240);
|
Debug.Assert(SampleCount is 160 or 240);
|
||||||
|
|
||||||
if (SampleCount == 160)
|
if (SampleCount == 160)
|
||||||
{
|
{
|
||||||
@@ -171,7 +171,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
|
|
||||||
public uint Estimate(MixCommand command)
|
public uint Estimate(MixCommand command)
|
||||||
{
|
{
|
||||||
Debug.Assert(SampleCount == 160 || SampleCount == 240);
|
Debug.Assert(SampleCount is 160 or 240);
|
||||||
|
|
||||||
if (SampleCount == 160)
|
if (SampleCount == 160)
|
||||||
{
|
{
|
||||||
@@ -183,7 +183,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
|
|
||||||
public virtual uint Estimate(DelayCommand command)
|
public virtual uint Estimate(DelayCommand command)
|
||||||
{
|
{
|
||||||
Debug.Assert(SampleCount == 160 || SampleCount == 240);
|
Debug.Assert(SampleCount is 160 or 240);
|
||||||
|
|
||||||
if (SampleCount == 160)
|
if (SampleCount == 160)
|
||||||
{
|
{
|
||||||
@@ -233,7 +233,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
|
|
||||||
public virtual uint Estimate(ReverbCommand command)
|
public virtual uint Estimate(ReverbCommand command)
|
||||||
{
|
{
|
||||||
Debug.Assert(SampleCount == 160 || SampleCount == 240);
|
Debug.Assert(SampleCount is 160 or 240);
|
||||||
|
|
||||||
if (SampleCount == 160)
|
if (SampleCount == 160)
|
||||||
{
|
{
|
||||||
@@ -283,7 +283,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
|
|
||||||
public virtual uint Estimate(Reverb3dCommand command)
|
public virtual uint Estimate(Reverb3dCommand command)
|
||||||
{
|
{
|
||||||
Debug.Assert(SampleCount == 160 || SampleCount == 240);
|
Debug.Assert(SampleCount is 160 or 240);
|
||||||
|
|
||||||
if (SampleCount == 160)
|
if (SampleCount == 160)
|
||||||
{
|
{
|
||||||
@@ -333,7 +333,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
|
|
||||||
public uint Estimate(AuxiliaryBufferCommand command)
|
public uint Estimate(AuxiliaryBufferCommand command)
|
||||||
{
|
{
|
||||||
Debug.Assert(SampleCount == 160 || SampleCount == 240);
|
Debug.Assert(SampleCount is 160 or 240);
|
||||||
|
|
||||||
if (SampleCount == 160)
|
if (SampleCount == 160)
|
||||||
{
|
{
|
||||||
@@ -355,7 +355,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
|
|
||||||
public uint Estimate(VolumeCommand command)
|
public uint Estimate(VolumeCommand command)
|
||||||
{
|
{
|
||||||
Debug.Assert(SampleCount == 160 || SampleCount == 240);
|
Debug.Assert(SampleCount is 160 or 240);
|
||||||
|
|
||||||
if (SampleCount == 160)
|
if (SampleCount == 160)
|
||||||
{
|
{
|
||||||
@@ -367,7 +367,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
|
|
||||||
public uint Estimate(CircularBufferSinkCommand command)
|
public uint Estimate(CircularBufferSinkCommand command)
|
||||||
{
|
{
|
||||||
Debug.Assert(SampleCount == 160 || SampleCount == 240);
|
Debug.Assert(SampleCount is 160 or 240);
|
||||||
|
|
||||||
float costPerBuffer = 770.26f;
|
float costPerBuffer = 770.26f;
|
||||||
float baseCost = 0f;
|
float baseCost = 0f;
|
||||||
@@ -382,7 +382,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
|
|
||||||
public uint Estimate(DownMixSurroundToStereoCommand command)
|
public uint Estimate(DownMixSurroundToStereoCommand command)
|
||||||
{
|
{
|
||||||
Debug.Assert(SampleCount == 160 || SampleCount == 240);
|
Debug.Assert(SampleCount is 160 or 240);
|
||||||
|
|
||||||
if (SampleCount == 160)
|
if (SampleCount == 160)
|
||||||
{
|
{
|
||||||
@@ -394,7 +394,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
|
|
||||||
public uint Estimate(UpsampleCommand command)
|
public uint Estimate(UpsampleCommand command)
|
||||||
{
|
{
|
||||||
Debug.Assert(SampleCount == 160 || SampleCount == 240);
|
Debug.Assert(SampleCount is 160 or 240);
|
||||||
|
|
||||||
if (SampleCount == 160)
|
if (SampleCount == 160)
|
||||||
{
|
{
|
||||||
@@ -406,8 +406,8 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
|
|
||||||
public uint Estimate(DeviceSinkCommand command)
|
public uint Estimate(DeviceSinkCommand command)
|
||||||
{
|
{
|
||||||
Debug.Assert(SampleCount == 160 || SampleCount == 240);
|
Debug.Assert(SampleCount is 160 or 240);
|
||||||
Debug.Assert(command.InputCount == 2 || command.InputCount == 6);
|
Debug.Assert(command.InputCount is 2 or 6);
|
||||||
|
|
||||||
if (command.InputCount == 2)
|
if (command.InputCount == 2)
|
||||||
{
|
{
|
||||||
@@ -429,7 +429,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
|
|
||||||
public uint Estimate(PcmFloatDataSourceCommandVersion1 command)
|
public uint Estimate(PcmFloatDataSourceCommandVersion1 command)
|
||||||
{
|
{
|
||||||
Debug.Assert(SampleCount == 160 || SampleCount == 240);
|
Debug.Assert(SampleCount is 160 or 240);
|
||||||
|
|
||||||
float costPerSample = 3490.9f;
|
float costPerSample = 3490.9f;
|
||||||
float baseCost = 10090.9f;
|
float baseCost = 10090.9f;
|
||||||
@@ -445,7 +445,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
|
|
||||||
public uint Estimate(DataSourceVersion2Command command)
|
public uint Estimate(DataSourceVersion2Command command)
|
||||||
{
|
{
|
||||||
Debug.Assert(SampleCount == 160 || SampleCount == 240);
|
Debug.Assert(SampleCount is 160 or 240);
|
||||||
|
|
||||||
(float baseCost, float costPerSample) = GetCostByFormat(SampleCount, command.SampleFormat, command.SrcQuality);
|
(float baseCost, float costPerSample) = GetCostByFormat(SampleCount, command.SampleFormat, command.SrcQuality);
|
||||||
|
|
||||||
@@ -454,7 +454,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
|
|
||||||
private static (float, float) GetCostByFormat(uint sampleCount, SampleFormat format, SampleRateConversionQuality quality)
|
private static (float, float) GetCostByFormat(uint sampleCount, SampleFormat format, SampleRateConversionQuality quality)
|
||||||
{
|
{
|
||||||
Debug.Assert(sampleCount == 160 || sampleCount == 240);
|
Debug.Assert(sampleCount is 160 or 240);
|
||||||
|
|
||||||
switch (format)
|
switch (format)
|
||||||
{
|
{
|
||||||
@@ -540,7 +540,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
|
|
||||||
private uint EstimateLimiterCommandCommon(LimiterParameter parameter, bool enabled)
|
private uint EstimateLimiterCommandCommon(LimiterParameter parameter, bool enabled)
|
||||||
{
|
{
|
||||||
Debug.Assert(SampleCount == 160 || SampleCount == 240);
|
Debug.Assert(SampleCount is 160 or 240);
|
||||||
|
|
||||||
if (SampleCount == 160)
|
if (SampleCount == 160)
|
||||||
{
|
{
|
||||||
@@ -590,14 +590,14 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
|
|
||||||
public uint Estimate(LimiterCommandVersion1 command)
|
public uint Estimate(LimiterCommandVersion1 command)
|
||||||
{
|
{
|
||||||
Debug.Assert(SampleCount == 160 || SampleCount == 240);
|
Debug.Assert(SampleCount is 160 or 240);
|
||||||
|
|
||||||
return EstimateLimiterCommandCommon(command.Parameter, command.IsEffectEnabled);
|
return EstimateLimiterCommandCommon(command.Parameter, command.IsEffectEnabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
public uint Estimate(LimiterCommandVersion2 command)
|
public uint Estimate(LimiterCommandVersion2 command)
|
||||||
{
|
{
|
||||||
Debug.Assert(SampleCount == 160 || SampleCount == 240);
|
Debug.Assert(SampleCount is 160 or 240);
|
||||||
|
|
||||||
if (!command.Parameter.StatisticsEnabled || !command.IsEffectEnabled)
|
if (!command.Parameter.StatisticsEnabled || !command.IsEffectEnabled)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
|
|
||||||
public override uint Estimate(MultiTapBiquadFilterCommand command)
|
public override uint Estimate(MultiTapBiquadFilterCommand command)
|
||||||
{
|
{
|
||||||
Debug.Assert(SampleCount == 160 || SampleCount == 240);
|
Debug.Assert(SampleCount is 160 or 240);
|
||||||
|
|
||||||
if (SampleCount == 160)
|
if (SampleCount == 160)
|
||||||
{
|
{
|
||||||
@@ -24,7 +24,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
|
|
||||||
public override uint Estimate(CaptureBufferCommand command)
|
public override uint Estimate(CaptureBufferCommand command)
|
||||||
{
|
{
|
||||||
Debug.Assert(SampleCount == 160 || SampleCount == 240);
|
Debug.Assert(SampleCount is 160 or 240);
|
||||||
|
|
||||||
if (SampleCount == 160)
|
if (SampleCount == 160)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
|
|
||||||
public override uint Estimate(DelayCommand command)
|
public override uint Estimate(DelayCommand command)
|
||||||
{
|
{
|
||||||
Debug.Assert(SampleCount == 160 || SampleCount == 240);
|
Debug.Assert(SampleCount is 160 or 240);
|
||||||
|
|
||||||
if (SampleCount == 160)
|
if (SampleCount == 160)
|
||||||
{
|
{
|
||||||
@@ -63,7 +63,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
|
|
||||||
public override uint Estimate(ReverbCommand command)
|
public override uint Estimate(ReverbCommand command)
|
||||||
{
|
{
|
||||||
Debug.Assert(SampleCount == 160 || SampleCount == 240);
|
Debug.Assert(SampleCount is 160 or 240);
|
||||||
|
|
||||||
if (SampleCount == 160)
|
if (SampleCount == 160)
|
||||||
{
|
{
|
||||||
@@ -113,7 +113,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
|
|
||||||
public override uint Estimate(Reverb3dCommand command)
|
public override uint Estimate(Reverb3dCommand command)
|
||||||
{
|
{
|
||||||
Debug.Assert(SampleCount == 160 || SampleCount == 240);
|
Debug.Assert(SampleCount is 160 or 240);
|
||||||
|
|
||||||
if (SampleCount == 160)
|
if (SampleCount == 160)
|
||||||
{
|
{
|
||||||
@@ -163,7 +163,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
|
|
||||||
public override uint Estimate(CompressorCommand command)
|
public override uint Estimate(CompressorCommand command)
|
||||||
{
|
{
|
||||||
Debug.Assert(SampleCount == 160 || SampleCount == 240);
|
Debug.Assert(SampleCount is 160 or 240);
|
||||||
|
|
||||||
if (SampleCount == 160)
|
if (SampleCount == 160)
|
||||||
{
|
{
|
||||||
@@ -241,7 +241,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
|
|
||||||
public override uint Estimate(BiquadFilterAndMixCommand command)
|
public override uint Estimate(BiquadFilterAndMixCommand command)
|
||||||
{
|
{
|
||||||
Debug.Assert(SampleCount == 160 || SampleCount == 240);
|
Debug.Assert(SampleCount is 160 or 240);
|
||||||
|
|
||||||
if (command.HasVolumeRamp)
|
if (command.HasVolumeRamp)
|
||||||
{
|
{
|
||||||
@@ -265,7 +265,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
|
|
||||||
public override uint Estimate(MultiTapBiquadFilterAndMixCommand command)
|
public override uint Estimate(MultiTapBiquadFilterAndMixCommand command)
|
||||||
{
|
{
|
||||||
Debug.Assert(SampleCount == 160 || SampleCount == 240);
|
Debug.Assert(SampleCount is 160 or 240);
|
||||||
|
|
||||||
if (command.HasVolumeRamp)
|
if (command.HasVolumeRamp)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -262,12 +262,8 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool
|
|||||||
return UpdateResult.Success;
|
return UpdateResult.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inParameter.CpuAddress == 0 || (inParameter.CpuAddress % PageSize) != 0)
|
if (inParameter.CpuAddress == 0 || (inParameter.CpuAddress % PageSize) != 0 ||
|
||||||
{
|
inParameter.Size == 0 || (inParameter.Size % PageSize) != 0)
|
||||||
return UpdateResult.InvalidParameter;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inParameter.Size == 0 || (inParameter.Size % PageSize) != 0)
|
|
||||||
{
|
{
|
||||||
return UpdateResult.InvalidParameter;
|
return UpdateResult.InvalidParameter;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -218,7 +218,8 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
|||||||
/// <returns>True if any biquad filter is enabled.</returns>
|
/// <returns>True if any biquad filter is enabled.</returns>
|
||||||
public bool IsBiquadFilterEnabled()
|
public bool IsBiquadFilterEnabled()
|
||||||
{
|
{
|
||||||
return _biquadFilters[0].Enable || _biquadFilters[1].Enable;
|
Span<BiquadFilterParameter> biquadFiltersSpan = _biquadFilters.AsSpan();
|
||||||
|
return biquadFiltersSpan[0].Enable || biquadFiltersSpan[1].Enable;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -162,9 +162,11 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
{
|
{
|
||||||
ref VoiceState currentVoiceState = ref context.GetState(i);
|
ref VoiceState currentVoiceState = ref context.GetState(i);
|
||||||
|
|
||||||
|
Span<int> channelResourceIdsSpan = parameter.ChannelResourceIds.AsSpan();
|
||||||
|
|
||||||
for (int channelResourceIndex = 0; channelResourceIndex < parameter.ChannelCount; channelResourceIndex++)
|
for (int channelResourceIndex = 0; channelResourceIndex < parameter.ChannelCount; channelResourceIndex++)
|
||||||
{
|
{
|
||||||
int channelId = parameter.ChannelResourceIds[channelResourceIndex];
|
int channelId = channelResourceIdsSpan[channelResourceIndex];
|
||||||
|
|
||||||
Debug.Assert(channelId >= 0 && channelId < context.GetCount());
|
Debug.Assert(channelId >= 0 && channelId < context.GetCount());
|
||||||
|
|
||||||
|
|||||||
@@ -126,9 +126,9 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
|
|||||||
_sortedVoices.Span[i] = i;
|
_sortedVoices.Span[i] = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
int[] sortedVoicesTemp = _sortedVoices[..(int)GetCount()].ToArray();
|
Span<int> sortedVoicesTemp = _sortedVoices[..(int)_voiceCount].Span;
|
||||||
|
|
||||||
Array.Sort(sortedVoicesTemp, (a, b) =>
|
sortedVoicesTemp.Sort((a, b) =>
|
||||||
{
|
{
|
||||||
ref VoiceState aState = ref GetState(a);
|
ref VoiceState aState = ref GetState(a);
|
||||||
ref VoiceState bState = ref GetState(b);
|
ref VoiceState bState = ref GetState(b);
|
||||||
@@ -143,7 +143,7 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
|
|||||||
return result;
|
return result;
|
||||||
});
|
});
|
||||||
|
|
||||||
sortedVoicesTemp.AsSpan().CopyTo(_sortedVoices.Span);
|
// sortedVoicesTemp.CopyTo(_sortedVoices.Span);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -219,15 +219,17 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void InitializeWaveBuffers()
|
private void InitializeWaveBuffers()
|
||||||
{
|
{
|
||||||
for (int i = 0; i < WaveBuffers.Length; i++)
|
Span<WaveBuffer> waveBuffersSpan = WaveBuffers.AsSpan();
|
||||||
|
|
||||||
|
for (int i = 0; i < waveBuffersSpan.Length; i++)
|
||||||
{
|
{
|
||||||
WaveBuffers[i].StartSampleOffset = 0;
|
waveBuffersSpan[i].StartSampleOffset = 0;
|
||||||
WaveBuffers[i].EndSampleOffset = 0;
|
waveBuffersSpan[i].EndSampleOffset = 0;
|
||||||
WaveBuffers[i].ShouldLoop = false;
|
waveBuffersSpan[i].ShouldLoop = false;
|
||||||
WaveBuffers[i].IsEndOfStream = false;
|
waveBuffersSpan[i].IsEndOfStream = false;
|
||||||
WaveBuffers[i].BufferAddressInfo.Setup(0, 0);
|
waveBuffersSpan[i].BufferAddressInfo.Setup(0, 0);
|
||||||
WaveBuffers[i].ContextAddressInfo.Setup(0, 0);
|
waveBuffersSpan[i].ContextAddressInfo.Setup(0, 0);
|
||||||
WaveBuffers[i].IsSendToAudioProcessor = true;
|
waveBuffersSpan[i].IsSendToAudioProcessor = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -447,9 +449,12 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
|
|||||||
|
|
||||||
ref VoiceUpdateState voiceUpdateState = ref voiceUpdateStates[0].Span[0];
|
ref VoiceUpdateState voiceUpdateState = ref voiceUpdateStates[0].Span[0];
|
||||||
|
|
||||||
|
Span<WaveBuffer> waveBuffersSpan = WaveBuffers.AsSpan();
|
||||||
|
Span<WaveBufferInternal> pWaveBuffersSpan = parameter.WaveBuffers.AsSpan();
|
||||||
|
|
||||||
for (int i = 0; i < Constants.VoiceWaveBufferCount; i++)
|
for (int i = 0; i < Constants.VoiceWaveBufferCount; i++)
|
||||||
{
|
{
|
||||||
UpdateWaveBuffer(errorInfos.AsSpan(i * 2, 2), ref WaveBuffers[i], ref parameter.WaveBuffers[i], parameter.SampleFormat, voiceUpdateState.IsWaveBufferValid[i], mapper, ref behaviourContext);
|
UpdateWaveBuffer(errorInfos.AsSpan(i * 2, 2), ref waveBuffersSpan[i], ref pWaveBuffersSpan[i], parameter.SampleFormat, voiceUpdateState.IsWaveBufferValid[i], mapper, ref behaviourContext);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -534,9 +539,11 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
|
|||||||
/// <param name="context">The voice context.</param>
|
/// <param name="context">The voice context.</param>
|
||||||
private void ResetResources(VoiceContext context)
|
private void ResetResources(VoiceContext context)
|
||||||
{
|
{
|
||||||
|
Span<int> channelResourceIdsSpan = ChannelResourceIds.AsSpan();
|
||||||
|
|
||||||
for (int i = 0; i < ChannelsCount; i++)
|
for (int i = 0; i < ChannelsCount; i++)
|
||||||
{
|
{
|
||||||
int channelResourceId = ChannelResourceIds[i];
|
int channelResourceId = channelResourceIdsSpan[i];
|
||||||
|
|
||||||
ref VoiceChannelResource voiceChannelResource = ref context.GetChannelResource(channelResourceId);
|
ref VoiceChannelResource voiceChannelResource = ref context.GetChannelResource(channelResourceId);
|
||||||
|
|
||||||
@@ -560,9 +567,11 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
|
|||||||
{
|
{
|
||||||
uint waveBufferIndex = WaveBuffersIndex;
|
uint waveBufferIndex = WaveBuffersIndex;
|
||||||
|
|
||||||
|
Span<WaveBuffer> waveBuffersSpan = WaveBuffers.AsSpan();
|
||||||
|
|
||||||
for (int i = 0; i < waveBufferCount; i++)
|
for (int i = 0; i < waveBufferCount; i++)
|
||||||
{
|
{
|
||||||
WaveBuffers[(int)waveBufferIndex].IsSendToAudioProcessor = true;
|
waveBuffersSpan[(int)waveBufferIndex].IsSendToAudioProcessor = true;
|
||||||
|
|
||||||
for (int j = 0; j < channelCount; j++)
|
for (int j = 0; j < channelCount; j++)
|
||||||
{
|
{
|
||||||
@@ -591,14 +600,18 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
|
|||||||
FlushWaveBufferCount = 0;
|
FlushWaveBufferCount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Span<WaveBuffer> waveBuffersSpan;
|
||||||
|
|
||||||
switch (PlayState)
|
switch (PlayState)
|
||||||
{
|
{
|
||||||
case PlayState.Started:
|
case PlayState.Started:
|
||||||
for (int i = 0; i < WaveBuffers.Length; i++)
|
waveBuffersSpan = WaveBuffers.AsSpan();
|
||||||
{
|
|
||||||
ref WaveBuffer wavebuffer = ref WaveBuffers[i];
|
|
||||||
|
|
||||||
if (!wavebuffer.IsSendToAudioProcessor)
|
for (int i = 0; i < waveBuffersSpan.Length; i++)
|
||||||
|
{
|
||||||
|
ref WaveBuffer waveBuffer = ref waveBuffersSpan[i];
|
||||||
|
|
||||||
|
if (!waveBuffer.IsSendToAudioProcessor)
|
||||||
{
|
{
|
||||||
for (int y = 0; y < ChannelsCount; y++)
|
for (int y = 0; y < ChannelsCount; y++)
|
||||||
{
|
{
|
||||||
@@ -607,7 +620,7 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
|
|||||||
voiceUpdateStates[y].Span[0].IsWaveBufferValid[i] = true;
|
voiceUpdateStates[y].Span[0].IsWaveBufferValid[i] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
wavebuffer.IsSendToAudioProcessor = true;
|
waveBuffer.IsSendToAudioProcessor = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -626,11 +639,13 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
case PlayState.Stopping:
|
case PlayState.Stopping:
|
||||||
for (int i = 0; i < WaveBuffers.Length; i++)
|
waveBuffersSpan = WaveBuffers.AsSpan();
|
||||||
{
|
|
||||||
ref WaveBuffer wavebuffer = ref WaveBuffers[i];
|
|
||||||
|
|
||||||
wavebuffer.IsSendToAudioProcessor = true;
|
for (int i = 0; i < waveBuffersSpan.Length; i++)
|
||||||
|
{
|
||||||
|
ref WaveBuffer waveBuffer = ref waveBuffersSpan[i];
|
||||||
|
|
||||||
|
waveBuffer.IsSendToAudioProcessor = true;
|
||||||
|
|
||||||
for (int j = 0; j < ChannelsCount; j++)
|
for (int j = 0; j < ChannelsCount; j++)
|
||||||
{
|
{
|
||||||
@@ -702,9 +717,11 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
|
|||||||
|
|
||||||
Memory<VoiceUpdateState>[] voiceUpdateStates = new Memory<VoiceUpdateState>[Constants.VoiceChannelCountMax];
|
Memory<VoiceUpdateState>[] voiceUpdateStates = new Memory<VoiceUpdateState>[Constants.VoiceChannelCountMax];
|
||||||
|
|
||||||
|
Span<int> channelResourceIdsSpan = ChannelResourceIds.AsSpan();
|
||||||
|
|
||||||
for (int i = 0; i < ChannelsCount; i++)
|
for (int i = 0; i < ChannelsCount; i++)
|
||||||
{
|
{
|
||||||
voiceUpdateStates[i] = context.GetUpdateStateForDsp(ChannelResourceIds[i]);
|
voiceUpdateStates[i] = context.GetUpdateStateForDsp(channelResourceIdsSpan[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return UpdateParametersForCommandGeneration(voiceUpdateStates);
|
return UpdateParametersForCommandGeneration(voiceUpdateStates);
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
|
||||||
<ProjectReference Include="..\Ryujinx.Cpu\Ryujinx.Cpu.csproj" />
|
<ProjectReference Include="..\Ryujinx.Cpu\Ryujinx.Cpu.csproj" />
|
||||||
<ProjectReference Include="..\Ryujinx.Memory\Ryujinx.Memory.csproj" />
|
<ProjectReference Include="..\Ryujinx.Memory\Ryujinx.Memory.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@@ -0,0 +1,226 @@
|
|||||||
|
namespace Ryujinx.Common.Collections
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a collection that can store 1 bit values.
|
||||||
|
/// </summary>
|
||||||
|
public struct BitMap
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Size in bits of the integer used internally for the groups of bits.
|
||||||
|
/// </summary>
|
||||||
|
public const int IntSize = 64;
|
||||||
|
|
||||||
|
private const int IntShift = 6;
|
||||||
|
private const int IntMask = IntSize - 1;
|
||||||
|
|
||||||
|
private readonly long[] _masks;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the value of a bit.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="bit">Bit to access</param>
|
||||||
|
/// <returns>Bit value</returns>
|
||||||
|
public bool this[int bit]
|
||||||
|
{
|
||||||
|
get => IsSet(bit);
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value)
|
||||||
|
{
|
||||||
|
Set(bit);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Clear(bit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new bitmap.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="count">Total number of bits</param>
|
||||||
|
public BitMap(int count)
|
||||||
|
{
|
||||||
|
_masks = new long[(count + IntMask) / IntSize];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if any bit is set.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>True if any bit is set, false otherwise</returns>
|
||||||
|
public bool AnySet()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < _masks.Length; i++)
|
||||||
|
{
|
||||||
|
if (_masks[i] != 0)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if a specific bit is set.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="bit">Bit to be checked</param>
|
||||||
|
/// <returns>True if set, false otherwise</returns>
|
||||||
|
public bool IsSet(int bit)
|
||||||
|
{
|
||||||
|
int wordIndex = bit >> IntShift;
|
||||||
|
int wordBit = bit & IntMask;
|
||||||
|
|
||||||
|
long wordMask = 1L << wordBit;
|
||||||
|
|
||||||
|
return (_masks[wordIndex] & wordMask) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if any bit inside a given range of bits is set.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="start">Start bit of the range</param>
|
||||||
|
/// <param name="end">End bit of the range (inclusive)</param>
|
||||||
|
/// <returns>True if any bit is set, false otherwise</returns>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the value of a bit to 1.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="bit">Bit to be set</param>
|
||||||
|
/// <returns>True if the bit was 0 and then changed to 1, false if it was already 1</returns>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets a given range of bits to 1.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="start">Start bit of the range</param>
|
||||||
|
/// <param name="end">End bit of the range (inclusive)</param>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets a given bit to 0.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="bit">Bit to be cleared</param>
|
||||||
|
public void Clear(int bit)
|
||||||
|
{
|
||||||
|
int wordIndex = bit >> IntShift;
|
||||||
|
int wordBit = bit & IntMask;
|
||||||
|
|
||||||
|
long wordMask = 1L << wordBit;
|
||||||
|
|
||||||
|
_masks[wordIndex] &= ~wordMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets all bits to 0.
|
||||||
|
/// </summary>
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < _masks.Length; i++)
|
||||||
|
{
|
||||||
|
_masks[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets one or more groups of bits to 0.
|
||||||
|
/// See <see cref="IntSize"/> for how many bits are inside each group.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="start">Start index of the group</param>
|
||||||
|
/// <param name="end">End index of the group (inclusive)</param>
|
||||||
|
public void ClearInt(int start, int end)
|
||||||
|
{
|
||||||
|
for (int i = start; i <= end; i++)
|
||||||
|
{
|
||||||
|
_masks[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -318,13 +318,16 @@ namespace Ryujinx.Common.Collections
|
|||||||
{
|
{
|
||||||
Root = newNode;
|
Root = newNode;
|
||||||
}
|
}
|
||||||
else if (start.CompareTo(parent.Start) < 0)
|
else if (parent != null && start.CompareTo(parent.Start) < 0)
|
||||||
{
|
{
|
||||||
parent.Left = newNode;
|
parent.Left = newNode;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
parent.Right = newNode;
|
if (parent != null)
|
||||||
|
{
|
||||||
|
parent.Right = newNode;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PropagateIncrease(newNode);
|
PropagateIncrease(newNode);
|
||||||
|
|||||||
@@ -14,12 +14,13 @@ namespace Ryujinx.Common.Collections
|
|||||||
/// Adds a new node into the tree.
|
/// Adds a new node into the tree.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="node">Node to be added</param>
|
/// <param name="node">Node to be added</param>
|
||||||
|
/// <param name="parent">Node to be added under</param>
|
||||||
/// <exception cref="ArgumentNullException"><paramref name="node"/> is null</exception>
|
/// <exception cref="ArgumentNullException"><paramref name="node"/> is null</exception>
|
||||||
public void Add(T node)
|
public void Add(T node, T parent = null)
|
||||||
{
|
{
|
||||||
ArgumentNullException.ThrowIfNull(node);
|
ArgumentNullException.ThrowIfNull(node);
|
||||||
|
|
||||||
Insert(node);
|
Insert(node, parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -75,9 +76,11 @@ namespace Ryujinx.Common.Collections
|
|||||||
/// Inserts a new node into the tree.
|
/// Inserts a new node into the tree.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="node">Node to be inserted</param>
|
/// <param name="node">Node to be inserted</param>
|
||||||
private void Insert(T node)
|
/// <param name="parent">Node to be inserted under</param>
|
||||||
|
private void Insert(T node, T parent = null)
|
||||||
{
|
{
|
||||||
T newNode = BSTInsert(node);
|
T newNode = parent != null ? InsertWithParent(node, parent) : BSTInsert(node);
|
||||||
|
|
||||||
RestoreBalanceAfterInsertion(newNode);
|
RestoreBalanceAfterInsertion(newNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,10 +123,77 @@ namespace Ryujinx.Common.Collections
|
|||||||
else if (newNode.CompareTo(parent) < 0)
|
else if (newNode.CompareTo(parent) < 0)
|
||||||
{
|
{
|
||||||
parent.Left = newNode;
|
parent.Left = newNode;
|
||||||
|
|
||||||
|
newNode.Successor = parent;
|
||||||
|
|
||||||
|
if (parent.Predecessor != null)
|
||||||
|
{
|
||||||
|
newNode.Predecessor = parent.Predecessor;
|
||||||
|
newNode.Predecessor.Successor = newNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
parent.Predecessor = newNode;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
parent.Right = newNode;
|
parent.Right = newNode;
|
||||||
|
|
||||||
|
newNode.Predecessor = parent;
|
||||||
|
|
||||||
|
if (parent.Successor != null)
|
||||||
|
{
|
||||||
|
newNode.Successor = parent.Successor;
|
||||||
|
newNode.Successor.Predecessor = newNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
parent.Successor = newNode;
|
||||||
|
}
|
||||||
|
Count++;
|
||||||
|
return newNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Insertion Mechanism for a Binary Search Tree (BST).
|
||||||
|
/// <br></br>
|
||||||
|
/// Inserts a new node directly under a parent node
|
||||||
|
/// where all children in the left subtree are less than <paramref name="newNode"/>,
|
||||||
|
/// and all children in the right subtree are greater than <paramref name="newNode"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="newNode">Node to be inserted</param>
|
||||||
|
/// <param name="parent">Node to be inserted under</param>
|
||||||
|
/// <returns>The inserted Node</returns>
|
||||||
|
private T InsertWithParent(T newNode, T parent)
|
||||||
|
{
|
||||||
|
newNode.Parent = parent;
|
||||||
|
|
||||||
|
if (newNode.CompareTo(parent) < 0)
|
||||||
|
{
|
||||||
|
parent.Left = newNode;
|
||||||
|
|
||||||
|
newNode.Successor = parent;
|
||||||
|
|
||||||
|
if (parent.Predecessor != null)
|
||||||
|
{
|
||||||
|
newNode.Predecessor = parent.Predecessor;
|
||||||
|
parent.Predecessor = newNode;
|
||||||
|
newNode.Predecessor.Successor = newNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
parent.Predecessor = newNode;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
parent.Right = newNode;
|
||||||
|
|
||||||
|
newNode.Predecessor = parent;
|
||||||
|
|
||||||
|
if (parent.Successor != null)
|
||||||
|
{
|
||||||
|
newNode.Successor = parent.Successor;
|
||||||
|
newNode.Successor.Predecessor = newNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
parent.Successor = newNode;
|
||||||
}
|
}
|
||||||
Count++;
|
Count++;
|
||||||
return newNode;
|
return newNode;
|
||||||
@@ -156,7 +226,7 @@ namespace Ryujinx.Common.Collections
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
T element = Minimum(RightOf(nodeToDelete));
|
T element = nodeToDelete.Successor;
|
||||||
|
|
||||||
child = RightOf(element);
|
child = RightOf(element);
|
||||||
parent = ParentOf(element);
|
parent = ParentOf(element);
|
||||||
@@ -184,6 +254,9 @@ namespace Ryujinx.Common.Collections
|
|||||||
element.Left = old.Left;
|
element.Left = old.Left;
|
||||||
element.Right = old.Right;
|
element.Right = old.Right;
|
||||||
element.Parent = old.Parent;
|
element.Parent = old.Parent;
|
||||||
|
element.Predecessor = old.Predecessor;
|
||||||
|
if (element.Predecessor != null)
|
||||||
|
element.Predecessor.Successor = element;
|
||||||
|
|
||||||
if (ParentOf(old) == null)
|
if (ParentOf(old) == null)
|
||||||
{
|
{
|
||||||
@@ -239,6 +312,11 @@ namespace Ryujinx.Common.Collections
|
|||||||
RestoreBalanceAfterRemoval(child);
|
RestoreBalanceAfterRemoval(child);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (old.Successor != null)
|
||||||
|
old.Successor.Predecessor = old.Predecessor;
|
||||||
|
if (old.Predecessor != null)
|
||||||
|
old.Predecessor.Successor = old.Successor;
|
||||||
|
|
||||||
return old;
|
return old;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,8 +9,7 @@ namespace Ryujinx.Common.Collections
|
|||||||
public T Left;
|
public T Left;
|
||||||
public T Right;
|
public T Right;
|
||||||
public T Parent;
|
public T Parent;
|
||||||
|
public T Predecessor;
|
||||||
public T Predecessor => IntrusiveRedBlackTreeImpl<T>.PredecessorOf((T)this);
|
public T Successor;
|
||||||
public T Successor => IntrusiveRedBlackTreeImpl<T>.SuccessorOf((T)this);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ namespace Ryujinx.Common.Collections
|
|||||||
Node<TKey, TValue> node = GetNode(key);
|
Node<TKey, TValue> node = GetNode(key);
|
||||||
if (node != null)
|
if (node != null)
|
||||||
{
|
{
|
||||||
Node<TKey, TValue> successor = SuccessorOf(node);
|
Node<TKey, TValue> successor = node.Successor;
|
||||||
|
|
||||||
return successor != null ? successor.Key : default;
|
return successor != null ? successor.Key : default;
|
||||||
}
|
}
|
||||||
@@ -124,7 +124,7 @@ namespace Ryujinx.Common.Collections
|
|||||||
Node<TKey, TValue> node = GetNode(key);
|
Node<TKey, TValue> node = GetNode(key);
|
||||||
if (node != null)
|
if (node != null)
|
||||||
{
|
{
|
||||||
Node<TKey, TValue> predecessor = PredecessorOf(node);
|
Node<TKey, TValue> predecessor = node.Predecessor;
|
||||||
|
|
||||||
return predecessor != null ? predecessor.Key : default;
|
return predecessor != null ? predecessor.Key : default;
|
||||||
}
|
}
|
||||||
@@ -132,11 +132,10 @@ namespace Ryujinx.Common.Collections
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds all the nodes in the dictionary as key/value pairs into <paramref name="list"/>.
|
/// Adds all the nodes in the dictionary as key/value pairs into a list.
|
||||||
/// <br></br>
|
/// <br></br>
|
||||||
/// The key/value pairs will be added in Level Order.
|
/// The key/value pairs will be added in Level Order.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="list">List to add the tree pairs into</param>
|
|
||||||
public List<KeyValuePair<TKey, TValue>> AsLevelOrderList()
|
public List<KeyValuePair<TKey, TValue>> AsLevelOrderList()
|
||||||
{
|
{
|
||||||
List<KeyValuePair<TKey, TValue>> list = [];
|
List<KeyValuePair<TKey, TValue>> list = [];
|
||||||
@@ -163,7 +162,7 @@ namespace Ryujinx.Common.Collections
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds all the nodes in the dictionary into <paramref name="list"/>.
|
/// Adds all the nodes in the dictionary into a list.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>A list of all KeyValuePairs sorted by Key Order</returns>
|
/// <returns>A list of all KeyValuePairs sorted by Key Order</returns>
|
||||||
public List<KeyValuePair<TKey, TValue>> AsList()
|
public List<KeyValuePair<TKey, TValue>> AsList()
|
||||||
@@ -275,17 +274,20 @@ namespace Ryujinx.Common.Collections
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Node<TKey, TValue> newNode = new(key, value, parent);
|
Node<TKey, TValue> newNode = new(key, value, parent);
|
||||||
if (newNode.Parent == null)
|
if (parent == null)
|
||||||
{
|
{
|
||||||
Root = newNode;
|
Root = newNode;
|
||||||
}
|
}
|
||||||
else if (key.CompareTo(parent.Key) < 0)
|
else if (parent != null && key.CompareTo(parent.Key) < 0)
|
||||||
{
|
{
|
||||||
parent.Left = newNode;
|
parent.Left = newNode;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
parent.Right = newNode;
|
if (parent != null)
|
||||||
|
{
|
||||||
|
parent.Right = newNode;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Count++;
|
Count++;
|
||||||
return newNode;
|
return newNode;
|
||||||
|
|||||||
@@ -273,7 +273,7 @@ namespace Ryujinx.Common.Configuration
|
|||||||
{
|
{
|
||||||
FileSystemInfo resolvedDirectoryInfo =
|
FileSystemInfo resolvedDirectoryInfo =
|
||||||
Directory.ResolveLinkTarget(correctApplicationDataDirectoryPath, true);
|
Directory.ResolveLinkTarget(correctApplicationDataDirectoryPath, true);
|
||||||
string resolvedPath = resolvedDirectoryInfo.FullName;
|
string resolvedPath = resolvedDirectoryInfo?.FullName;
|
||||||
Logger.Error?.Print(LogClass.Application, $"Please manually move your Ryujinx data from {resolvedPath} to {correctApplicationDataDirectoryPath}, and remove the symlink.");
|
Logger.Error?.Print(LogClass.Application, $"Please manually move your Ryujinx data from {resolvedPath} to {correctApplicationDataDirectoryPath}, and remove the symlink.");
|
||||||
}
|
}
|
||||||
catch (Exception symlinkException)
|
catch (Exception symlinkException)
|
||||||
@@ -297,7 +297,7 @@ namespace Ryujinx.Common.Configuration
|
|||||||
{
|
{
|
||||||
FileSystemInfo resolvedDirectoryInfo =
|
FileSystemInfo resolvedDirectoryInfo =
|
||||||
Directory.ResolveLinkTarget(correctApplicationDataDirectoryPath, true);
|
Directory.ResolveLinkTarget(correctApplicationDataDirectoryPath, true);
|
||||||
string resolvedPath = resolvedDirectoryInfo.FullName;
|
string resolvedPath = resolvedDirectoryInfo?.FullName;
|
||||||
Logger.Error?.Print(LogClass.Application, $"Please manually move your Ryujinx data from {resolvedPath} to {correctApplicationDataDirectoryPath}, and remove the symlink.");
|
Logger.Error?.Print(LogClass.Application, $"Please manually move your Ryujinx data from {resolvedPath} to {correctApplicationDataDirectoryPath}, and remove the symlink.");
|
||||||
}
|
}
|
||||||
catch (Exception symlinkException)
|
catch (Exception symlinkException)
|
||||||
|
|||||||
@@ -46,10 +46,11 @@ namespace Ryujinx.Common.Configuration.Hid.Controller
|
|||||||
// PS5 touchpad button
|
// PS5 touchpad button
|
||||||
Touchpad,
|
Touchpad,
|
||||||
|
|
||||||
// Virtual buttons for single joycon
|
// Virtual buttons for single joycon (left)
|
||||||
SingleLeftTrigger0,
|
SingleLeftTrigger0,
|
||||||
SingleRightTrigger0,
|
SingleRightTrigger0,
|
||||||
|
|
||||||
|
// Virtual buttons for single joycon (right)
|
||||||
SingleLeftTrigger1,
|
SingleLeftTrigger1,
|
||||||
SingleRightTrigger1,
|
SingleRightTrigger1,
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -25,7 +25,7 @@ namespace Ryujinx.Common.Configuration.Hid.Controller.Motion
|
|||||||
{
|
{
|
||||||
string propertyName = tempReader.GetString();
|
string propertyName = tempReader.GetString();
|
||||||
|
|
||||||
if (propertyName.Equals("motion_backend"))
|
if (propertyName is "motion_backend")
|
||||||
{
|
{
|
||||||
tempReader.Read();
|
tempReader.Read();
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ namespace Ryujinx.Common.Configuration.Hid
|
|||||||
{
|
{
|
||||||
string propertyName = tempReader.GetString();
|
string propertyName = tempReader.GetString();
|
||||||
|
|
||||||
if (propertyName.Equals("backend"))
|
if (propertyName is "backend")
|
||||||
{
|
{
|
||||||
tempReader.Read();
|
tempReader.Read();
|
||||||
|
|
||||||
|
|||||||
@@ -13,5 +13,7 @@ namespace Ryujinx.Common.Configuration.Hid
|
|||||||
public Key VolumeDown { get; set; }
|
public Key VolumeDown { get; set; }
|
||||||
public Key CustomVSyncIntervalIncrement { get; set; }
|
public Key CustomVSyncIntervalIncrement { get; set; }
|
||||||
public Key CustomVSyncIntervalDecrement { get; set; }
|
public Key CustomVSyncIntervalDecrement { get; set; }
|
||||||
|
public Key TurboMode { get; set; }
|
||||||
|
public bool TurboModeWhileHeld { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user