mirror of
https://git.ryujinx.app/projects/Kenji-NX.git
synced 2026-09-27 20:07:54 +02:00
Compare commits
279
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
280c427c55 | ||
|
|
25b4892981 | ||
|
|
d16d67f7c4 | ||
|
|
e7cb0632c5 | ||
|
|
4ea8c0b280 | ||
|
|
aef309800c | ||
|
|
a008cd20f1 | ||
|
|
ec6ce46357 | ||
|
|
a7a3a17b48 | ||
|
|
94cafdd1f9 | ||
|
|
1b57022694 | ||
|
|
2bb07a4364 | ||
|
|
5e67569a6e | ||
|
|
97f0336aa2 | ||
|
|
53d32a761e | ||
|
|
02d8eeb18f | ||
|
|
54e2f79c16 | ||
|
|
77277442a9 | ||
|
|
b707c73f83 | ||
|
|
235fb5f8c2 | ||
|
|
c2cbd53235 | ||
|
|
4f3c3a7b5d | ||
|
|
2dc1894e1e | ||
|
|
db4799e7ab | ||
|
|
06f049b874 | ||
|
|
58f9b6106f | ||
|
|
5422aa35b9 | ||
|
|
8ccf83d9a3 | ||
|
|
2eecc18dc7 | ||
|
|
3ceca9c381 | ||
|
|
a4412346a7 | ||
|
|
b0eb271b5b | ||
|
|
88cef7e853 | ||
|
|
69c088004a | ||
|
|
d3bd888885 | ||
|
|
b615f50701 | ||
|
|
d5524be27a | ||
|
|
43b4f3bf12 | ||
|
|
8d7d8f344c | ||
|
|
047dbf3db7 | ||
|
|
d0f0007421 | ||
|
|
60e553bffa | ||
|
|
a8a23b6b2c | ||
|
|
e0f6e207e8 | ||
|
|
30ab86cb72 | ||
|
|
2901f9b795 | ||
|
|
b08b9eabe6 | ||
|
|
83c65c2c06 | ||
|
|
3a6ce7cd1e | ||
|
|
94cfe6930c | ||
|
|
9a4bf8f94d | ||
|
|
d69d9c3b31 | ||
|
|
d9da38c10b | ||
|
|
ac699e430b | ||
|
|
89d7a5c45e | ||
|
|
93d1eea58d | ||
|
|
ad8ab179d2 | ||
|
|
18c6ec439b | ||
|
|
f2ac50360e | ||
|
|
8c42f306dd | ||
|
|
d765d6daa6 | ||
|
|
e7c720fd2c | ||
|
|
f3a87af51d | ||
|
|
1b9615983f | ||
|
|
d040492b7a | ||
|
|
05f7878699 | ||
|
|
0d8bd2653b | ||
|
|
b7ffcb3be8 | ||
|
|
46c55ee79d | ||
|
|
6c1c05dc5d | ||
|
|
637c8130a3 | ||
|
|
7781911baa | ||
|
|
bd3d4d0566 | ||
|
|
63c7d2a0ee | ||
|
|
5f40a22b0d | ||
|
|
72955d1aa4 | ||
|
|
ce2f2a14af | ||
|
|
b4d0f10568 | ||
|
|
c2dfc93fa6 | ||
|
|
29a828c4ef | ||
|
|
9995e26ec6 | ||
|
|
59c277bc6c | ||
|
|
491fb3ccfb | ||
|
|
e96823c9a0 | ||
|
|
ad0e2c0dea | ||
|
|
59b1989b97 | ||
|
|
40c32c7a20 | ||
|
|
bfe7973d90 | ||
|
|
14c432a416 | ||
|
|
e35ac3fed0 | ||
|
|
e3e46315f0 | ||
|
|
a6cb810a92 | ||
|
|
21b2b2bc03 | ||
|
|
d3a6d02ca1 | ||
|
|
1b2d471039 | ||
|
|
890d5a6e2c | ||
|
|
79ab37f282 | ||
|
|
487ef4c163 | ||
|
|
69908d7bf1 | ||
|
|
395081ae87 | ||
|
|
1a7be99263 | ||
|
|
d7770e2582 | ||
|
|
6a59302104 | ||
|
|
e49ef8d60e | ||
|
|
6840ae0c5c | ||
|
|
6f5b351fa0 | ||
|
|
6d36f471dd | ||
|
|
2c1b7bc667 | ||
|
|
dc4a51a462 | ||
|
|
ea7d680d11 | ||
|
|
fcf1ba6b9e | ||
|
|
99435c8708 | ||
|
|
74011bbb45 | ||
|
|
066afe9407 | ||
|
|
8aa08bc64b | ||
|
|
940d78a1ad | ||
|
|
d0e82aafb1 | ||
|
|
fe7b601826 | ||
|
|
9063b4b8db | ||
|
|
e036837211 | ||
|
|
c6deb3800f | ||
|
|
75220096e7 | ||
|
|
7f805f59fd | ||
|
|
ab571e81b3 | ||
|
|
f6adcd2f65 | ||
|
|
85e05bc7b2 | ||
|
|
bb0658bba9 | ||
|
|
50b00e3827 | ||
|
|
99c2e672c3 | ||
|
|
9ea27c3318 | ||
|
|
203ac3a175 | ||
|
|
bc0c483041 | ||
|
|
dbaf317f15 | ||
|
|
9357bb1524 | ||
|
|
734aecd711 | ||
|
|
1af7ebafc8 | ||
|
|
c8a6958ef3 | ||
|
|
377805579b | ||
|
|
35bced8527 | ||
|
|
c1e24961f9 | ||
|
|
2d720fea03 | ||
|
|
65c740ec4a | ||
|
|
0b8d1d5816 | ||
|
|
78db4c365f | ||
|
|
1bb1c7bba8 | ||
|
|
e1b6cb71f8 | ||
|
|
f34ab9f043 | ||
|
|
ebdc2a9e81 | ||
|
|
ebeb6e146c | ||
|
|
28b4f45645 | ||
|
|
f3931a789a | ||
|
|
42369a22a3 | ||
|
|
7b801bb9d3 | ||
|
|
457b7e77de | ||
|
|
169712a9e8 | ||
|
|
a187ec09d7 | ||
|
|
6d9e7fea75 | ||
|
|
ea89fc28da | ||
|
|
7242cb2449 | ||
|
|
4f101170f0 | ||
|
|
fc7932ad27 | ||
|
|
0c41346400 | ||
|
|
c6230fc02e | ||
|
|
1ef09717d2 | ||
|
|
41eb6f5dc4 | ||
|
|
f47be3342f | ||
|
|
4d1620c330 | ||
|
|
7a5b5dee6a | ||
|
|
fa5b5b127e | ||
|
|
327416c967 | ||
|
|
c42663115b | ||
|
|
09bc10b15d | ||
|
|
75058d850b | ||
|
|
ab6ccb3d0b | ||
|
|
07ae239130 | ||
|
|
548ec2c175 | ||
|
|
925ffc3976 | ||
|
|
cf12e5ab82 | ||
|
|
1f283b366b | ||
|
|
e4d2dea201 | ||
|
|
0cf2b1fbda | ||
|
|
8418025b82 | ||
|
|
316dc3a9b3 | ||
|
|
d73d82fbc0 | ||
|
|
7cc28d0242 | ||
|
|
92c3b72b17 | ||
|
|
8707fd5232 | ||
|
|
c125d08636 | ||
|
|
eee4a6272c | ||
|
|
be176fd367 | ||
|
|
48ba1cc9ed | ||
|
|
a6f3f0718d | ||
|
|
9cdd9f6dbf | ||
|
|
0928851966 | ||
|
|
d773bd60f4 | ||
|
|
dfb6164ba5 | ||
|
|
5a9bc0d703 | ||
|
|
2699bcf03e | ||
|
|
f45c1e6dcb | ||
|
|
fd75b35496 | ||
|
|
3e8d562182 | ||
|
|
65caa1e3f2 | ||
|
|
e3ef1e1fde | ||
|
|
3deddbd491 | ||
|
|
ff0daa9f35 | ||
|
|
faa8fb6642 | ||
|
|
6e7d013903 | ||
|
|
5db4793c7b | ||
|
|
db8b61106d | ||
|
|
55ea9046a2 | ||
|
|
a84dd77dc2 | ||
|
|
743eef7ff9 | ||
|
|
39066b846d | ||
|
|
307d048d25 | ||
|
|
7f89429b9f | ||
|
|
6b08ba47c4 | ||
|
|
f63cb962ad | ||
|
|
2c2b37678a | ||
|
|
6fed0795b9 | ||
|
|
c5540f9541 | ||
|
|
4c7a5a7261 | ||
|
|
925610723c | ||
|
|
bdc573ab17 | ||
|
|
f61e340a58 | ||
|
|
0149b71ac9 | ||
|
|
b4eaa5f262 | ||
|
|
2f7406aaca | ||
|
|
a9954c23cc | ||
|
|
9955191651 | ||
|
|
2e0bb4ec56 | ||
|
|
c417740beb | ||
|
|
0d51f4fcb5 | ||
|
|
4626aae70e | ||
|
|
d0abbc6da3 | ||
|
|
9b6b9146e2 | ||
|
|
4605c8bbd3 | ||
|
|
18743ed661 | ||
|
|
3487c4546e | ||
|
|
c457a2470f | ||
|
|
9fb338395a | ||
|
|
ab7e27a4e9 | ||
|
|
44b6f162c1 | ||
|
|
775c7b766c | ||
|
|
3faa22b85a | ||
|
|
05c3f2a250 | ||
|
|
8611e42a2e | ||
|
|
620666909f | ||
|
|
ef6f43825d | ||
|
|
d7b797bf02 | ||
|
|
0e4f71b2f1 | ||
|
|
1c23a55937 | ||
|
|
84d7f2d113 | ||
|
|
78b00e530c | ||
|
|
b22d771cce | ||
|
|
81aa3b769c | ||
|
|
fda90239bc | ||
|
|
3af88ad2e6 | ||
|
|
d73be3b927 | ||
|
|
cc0b704d8e | ||
|
|
cdd4557343 | ||
|
|
e172bdf2eb | ||
|
|
6e7808a65a | ||
|
|
5a86fb9826 | ||
|
|
45166424fc | ||
|
|
965c18225b | ||
|
|
2ea7d32b30 | ||
|
|
40cbd19bc3 | ||
|
|
0255c1f463 | ||
|
|
3657e55c70 | ||
|
|
af3aa8f786 | ||
|
|
81728f62bb | ||
|
|
a4856f1885 | ||
|
|
8fcf430ea8 | ||
|
|
fc2f1588d5 | ||
|
|
6f13e04a24 | ||
|
|
4e669ada26 | ||
|
|
575cc233c9 | ||
|
|
7a4017d164 | ||
|
|
e8e4c5aa61 |
@@ -1,35 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
|
||||||
"extends": [
|
|
||||||
"renovate/config"
|
|
||||||
],
|
|
||||||
"enabledManagers": ["nuget"],
|
|
||||||
"packageRules": [
|
|
||||||
{
|
|
||||||
// require approval for *all* NuGet package updates, not just major versions.
|
|
||||||
"matchDepTypes": "nuget",
|
|
||||||
"dependencyDashboardApproval": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Ignore Gommon for automatic updates. I make breaking changes on minor updates not infrequently.
|
|
||||||
"matchDepNames": "Gommon",
|
|
||||||
"matchDepTypes": "nuget",
|
|
||||||
"enabled": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "group Silk.NET packages",
|
|
||||||
"extends": ["renovate/config//groups/silkdotnet.json"],
|
|
||||||
"groupName": "Silk.NET"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "group OpenTK packages",
|
|
||||||
"extends": ["renovate/config//groups/opentk.json"],
|
|
||||||
"groupName": "OpenTK"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "group Svg.Controls packages",
|
|
||||||
"extends": ["renovate/config//groups/svgcontrols.json"],
|
|
||||||
"groupName": "Svg.Controls"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
+3
-4
@@ -16,10 +16,6 @@ x64/
|
|||||||
build/
|
build/
|
||||||
[Bb]in/
|
[Bb]in/
|
||||||
[Oo]bj/
|
[Oo]bj/
|
||||||
AppDir/
|
|
||||||
publish_appimage/
|
|
||||||
publish_ava/
|
|
||||||
publish_tmp_ava/
|
|
||||||
|
|
||||||
# Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets
|
# Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets
|
||||||
!packages/*/build/
|
!packages/*/build/
|
||||||
@@ -177,3 +173,6 @@ PublishProfiles/
|
|||||||
|
|
||||||
# Glade backup files
|
# Glade backup files
|
||||||
*.glade~
|
*.glade~
|
||||||
|
|
||||||
|
# Log files
|
||||||
|
/logs/
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
[submodule "src/KenjinxAndroid/app/src/main/cpp/libraries/adrenotools"]
|
||||||
|
path = src/KenjinxAndroid/app/src/main/cpp/libraries/adrenotools
|
||||||
|
url = https://git.ryujinx.app/kenji-nx/libadrenotools.git
|
||||||
|
[submodule "src/KenjinxAndroid/app/src/main/cpp/libraries/openal"]
|
||||||
|
path = src/KenjinxAndroid/app/src/main/cpp/libraries/openal
|
||||||
|
url = https://git.ryujinx.app/kenji-nx/openal-soft.git
|
||||||
|
[submodule "src/KenjinxAndroid/app/src/main/cpp/libraries/adrenotools/lib/linkernsbypass"]
|
||||||
|
path = src/KenjinxAndroid/app/src/main/cpp/libraries/adrenotools/lib/linkernsbypass
|
||||||
|
url = https://git.ryujinx.app/kenji-nx/liblinkernsbypass.git
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
<Project>
|
<Project>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net10.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<LangVersion>latest</LangVersion>
|
<LangVersion>latest</LangVersion>
|
||||||
<DebugType Condition="'$(Configuration)'=='Release'">none</DebugType>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
+23
-21
@@ -3,41 +3,41 @@
|
|||||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageVersion Include="Avalonia" Version="12.1.1" />
|
<PackageVersion Include="Avalonia" Version="11.3.13" />
|
||||||
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="12.1.2" />
|
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.3.13" />
|
||||||
<PackageVersion Include="Avalonia.Desktop" Version="12.1.1" />
|
<PackageVersion Include="Avalonia.Desktop" Version="11.3.13" />
|
||||||
<PackageVersion Include="AvaloniaUI.DiagnosticsSupport" Version="2.2.3" />
|
<PackageVersion Include="Avalonia.Diagnostics" Version="11.3.13" />
|
||||||
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="12.1.1" />
|
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="11.3.13" />
|
||||||
<PackageVersion Include="Svg.Controls.Avalonia" Version="12.0.0.13" />
|
<PackageVersion Include="Svg.Controls.Avalonia" Version="11.3.9.5" />
|
||||||
<PackageVersion Include="Svg.Controls.Skia.Avalonia" Version="12.0.0.13" />
|
<PackageVersion Include="Svg.Controls.Skia.Avalonia" Version="11.3.9.5" />
|
||||||
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
|
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
|
||||||
<PackageVersion Include="CommunityToolkit.Mvvm" Version="8.4.2" />
|
|
||||||
<PackageVersion Include="Concentus" Version="2.2.2" />
|
<PackageVersion Include="Concentus" Version="2.2.2" />
|
||||||
<PackageVersion Include="DiscordRichPresence" Version="1.6.1.70" />
|
<PackageVersion Include="DiscordRichPresence" Version="1.6.1.70" />
|
||||||
<PackageVersion Include="DynamicData" Version="9.4.33" />
|
<PackageVersion Include="DynamicData" Version="9.4.31" />
|
||||||
<PackageVersion Include="FluentAvaloniaUI" Version="3.0.2" />
|
<PackageVersion Include="FluentAvaloniaUI" Version="2.4.1" />
|
||||||
<PackageVersion Include="Gommon" Version="2.8.1.2" />
|
<PackageVersion Include="Gommon" Version="2.8.1.1" />
|
||||||
<PackageVersion Include="Humanizer" Version="3.0.10" />
|
<PackageVersion Include="Humanizer" Version="3.0.10" />
|
||||||
<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.12.0" />
|
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.12.0" />
|
||||||
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.21.0" />
|
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.17.0" />
|
||||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.8.1" />
|
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.3.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="Newtonsoft.Json" Version="13.0.4" />
|
<PackageVersion Include="Newtonsoft.Json" Version="13.0.4" />
|
||||||
<PackageVersion Include="NUnit" Version="4.6.1" />
|
<PackageVersion Include="NUnit" Version="4.5.1" />
|
||||||
<PackageVersion Include="NUnit3TestAdapter" Version="6.2.0" />
|
<PackageVersion Include="NUnit3TestAdapter" Version="6.2.0" />
|
||||||
<PackageVersion Include="OpenTK.Core" Version="4.9.4" />
|
<PackageVersion Include="OpenTK.Core" Version="4.9.4" />
|
||||||
<PackageVersion Include="OpenTK.Graphics" Version="4.9.4" />
|
<PackageVersion Include="OpenTK.Graphics" Version="4.9.4" />
|
||||||
<PackageVersion Include="OpenTK.Audio.OpenAL" Version="4.9.4" />
|
<PackageVersion Include="OpenTK.Audio.OpenAL" Version="4.9.4" />
|
||||||
|
<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" Version="1.25.2" />
|
<PackageVersion Include="Ryujinx.Audio.OpenAL" Version="1.25.2" />
|
||||||
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies.Linux" Version="6.1.4-build6" />
|
<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.macOS" Version="5.0.3-build14" />
|
||||||
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies.Windows" Version="6.1.4-build6" />
|
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies.Windows" Version="6.1.3-build5" />
|
||||||
<PackageVersion Include="Ryujinx.Graphics.Vulkan.MoltenVK" Version="1.4.2-ryujinx.6" />
|
<PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" />
|
||||||
<PackageVersion Include="Ryujinx.LibHac" Version="0.21.0-alpha.133" />
|
<PackageVersion Include="Ryujinx.LibHac" Version="0.21.0-alpha.128" />
|
||||||
<PackageVersion Include="Ryujinx.SDL3-CS" Version="2026.707.0" />
|
<PackageVersion Include="Ryujinx.SDL3-CS" Version="2026.707.0" />
|
||||||
<PackageVersion Include="securifybv.ShellLink" Version="0.1.0" />
|
<PackageVersion Include="securifybv.ShellLink" Version="0.1.0" />
|
||||||
<PackageVersion Include="SharpZipLib" Version="1.4.2" />
|
<PackageVersion Include="SharpZipLib" Version="1.4.2" />
|
||||||
@@ -45,10 +45,12 @@
|
|||||||
<PackageVersion Include="Silk.NET.Vulkan" Version="2.23.0" />
|
<PackageVersion Include="Silk.NET.Vulkan" Version="2.23.0" />
|
||||||
<PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.23.0" />
|
<PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.23.0" />
|
||||||
<PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.23.0" />
|
<PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.23.0" />
|
||||||
<PackageVersion Include="SkiaSharp" Version="3.119.4" />
|
<PackageVersion Include="SkiaSharp" Version="2.88.9" />
|
||||||
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="3.119.4" />
|
<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="10.0.10" />
|
<PackageVersion Include="System.IO.Hashing" Version="9.0.12" />
|
||||||
|
<PackageVersion Include="System.Management" Version="9.0.12" />
|
||||||
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.1.0" />
|
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.1.0" />
|
||||||
|
<PackageVersion Include="Rxmxnx.PInvoke.Extensions" Version="2.9.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
+261
@@ -0,0 +1,261 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 17
|
||||||
|
VisualStudioVersion = 17.1.32228.430
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Tests", "src\Ryujinx.Tests\Ryujinx.Tests.csproj", "{EBB55AEA-C7D7-4DEB-BF96-FA1789E225E9}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LibKenjinx", "src\LibKenjinx\LibKenjinx.csproj", "{AF58C1D5-DE16-429B-B155-7CE83B4E8FA6}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Tests.Unicorn", "src\Ryujinx.Tests.Unicorn\Ryujinx.Tests.Unicorn.csproj", "{D8F72938-78EF-4E8C-BAFE-531C9C3C8F15}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.HLE", "src\Ryujinx.HLE\Ryujinx.HLE.csproj", "{CB92CFF9-1D62-4D4F-9E88-8130EF61E351}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.ShaderTools", "src\Ryujinx.ShaderTools\Ryujinx.ShaderTools.csproj", "{3AB294D0-2230-468F-9EB3-BDFCAEAE99A5}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Common", "src\Ryujinx.Common\Ryujinx.Common.csproj", "{5FD4E4F6-8928-4B3C-BE07-28A675C17226}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ARMeilleure", "src\ARMeilleure\ARMeilleure.csproj", "{ABF09A5E-2D8B-4B6F-A51D-5CE414DDB15A}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Gpu", "src\Ryujinx.Graphics.Gpu\Ryujinx.Graphics.Gpu.csproj", "{ADA7EA87-0D63-4D97-9433-922A2124401F}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.GAL", "src\Ryujinx.Graphics.GAL\Ryujinx.Graphics.GAL.csproj", "{A602AE97-91A5-4608-8DF1-EBF4ED7A0B9E}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.OpenGL", "src\Ryujinx.Graphics.OpenGL\Ryujinx.Graphics.OpenGL.csproj", "{9558FB96-075D-4219-8FFF-401979DC0B69}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Texture", "src\Ryujinx.Graphics.Texture\Ryujinx.Graphics.Texture.csproj", "{E1B1AD28-289D-47B7-A106-326972240207}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Shader", "src\Ryujinx.Graphics.Shader\Ryujinx.Graphics.Shader.csproj", "{03B955CD-AD84-4B93-AAA7-BF17923BBAA5}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Nvdec", "src\Ryujinx.Graphics.Nvdec\Ryujinx.Graphics.Nvdec.csproj", "{85A0FA56-DC01-4A42-8808-70DAC76BD66D}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio", "src\Ryujinx.Audio\Ryujinx.Audio.csproj", "{806ACF6D-90B0-45D0-A1AC-5F220F3B3985}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{36F870C1-3E5F-485F-B426-F0645AF78751}"
|
||||||
|
ProjectSection(SolutionItems) = preProject
|
||||||
|
.editorconfig = .editorconfig
|
||||||
|
Directory.Build.props = Directory.Build.props
|
||||||
|
Directory.Packages.props = Directory.Packages.props
|
||||||
|
nuget.config = nuget.config
|
||||||
|
EndProjectSection
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Memory", "src\Ryujinx.Memory\Ryujinx.Memory.csproj", "{A5E6C691-9E22-4263-8F40-42F002CE66BE}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Tests.Memory", "src\Ryujinx.Tests.Memory\Ryujinx.Tests.Memory.csproj", "{D1CC5322-7325-4F6B-9625-194B30BE1296}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Cpu", "src\Ryujinx.Cpu\Ryujinx.Cpu.csproj", "{3DF35E3D-D844-4399-A9A1-A9E923264C17}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Device", "src\Ryujinx.Graphics.Device\Ryujinx.Graphics.Device.csproj", "{C3002C3C-7B09-4FE7-894A-372EDA22FC6E}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Host1x", "src\Ryujinx.Graphics.Host1x\Ryujinx.Graphics.Host1x.csproj", "{C35F1536-7DE5-4F9D-9604-B5B4E1561947}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Nvdec.Vp9", "src\Ryujinx.Graphics.Nvdec.Vp9\Ryujinx.Graphics.Nvdec.Vp9.csproj", "{B9AECA11-E248-4886-A10B-81B631CAAF29}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Vic", "src\Ryujinx.Graphics.Vic\Ryujinx.Graphics.Vic.csproj", "{81BB2C11-9408-4EA3-822E-42987AF54429}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Video", "src\Ryujinx.Graphics.Video\Ryujinx.Graphics.Video.csproj", "{FD4A2C14-8E3D-4957-ABBE-3C38897B3E2D}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio.Backends.OpenAL", "src\Ryujinx.Audio.Backends.OpenAL\Ryujinx.Audio.Backends.OpenAL.csproj", "{0BE11899-DF2D-4BDE-B9EE-2489E8D35E7D}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio.Backends.SoundIo", "src\Ryujinx.Audio.Backends.SoundIo\Ryujinx.Audio.Backends.SoundIo.csproj", "{716364DE-B988-41A6-BAB4-327964266ECC}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Input", "src\Ryujinx.Input\Ryujinx.Input.csproj", "{C16F112F-38C3-40BC-9F5F-4791112063D6}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Input.SDL2", "src\Ryujinx.Input.SDL2\Ryujinx.Input.SDL2.csproj", "{DFAB6F2D-B9BF-4AFF-B22B-7684A328EBA3}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.SDL2.Common", "src\Ryujinx.SDL2.Common\Ryujinx.SDL2.Common.csproj", "{2D5D3A1D-5730-4648-B0AB-06C53CB910C0}"
|
||||||
|
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}"
|
||||||
|
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}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx", "src\Ryujinx\Ryujinx.csproj", "{7C1B2721-13DA-4B62-B046-C626605ECCE6}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.UI.Common", "src\Ryujinx.UI.Common\Ryujinx.UI.Common.csproj", "{BA161CA0-CD65-4E6E-B644-51C8D1E542DC}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Generators", "src\Ryujinx.Horizon.Generators\Ryujinx.Horizon.Generators.csproj", "{6AE2A5E8-4C5A-48B9-997B-E1455C0355C6}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Vulkan", "src\Ryujinx.Graphics.Vulkan\Ryujinx.Graphics.Vulkan.csproj", "{D4D09B08-D580-4D69-B886-C35D2853F6C8}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Spv.Generator", "src\Spv.Generator\Spv.Generator.csproj", "{2BCB3D7A-38C0-4FE7-8FDA-374C6AD56D0E}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.UI.LocaleGenerator", "src\Ryujinx.UI.LocaleGenerator\Ryujinx.UI.LocaleGenerator.csproj", "{77D01AD9-2C98-478E-AE1D-8F7100738FB4}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Common", "src\Ryujinx.Horizon.Common\Ryujinx.Horizon.Common.csproj", "{77F96ECE-4952-42DB-A528-DED25572A573}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon", "src\Ryujinx.Horizon\Ryujinx.Horizon.csproj", "{AF34127A-3A92-43E5-8496-14960A50B1F1}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Kernel.Generators", "src\Ryujinx.Horizon.Kernel.Generators\Ryujinx.Horizon.Kernel.Generators.csproj", "{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.HLE.Generators", "src\Ryujinx.HLE.Generators\Ryujinx.HLE.Generators.csproj", "{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{EBB55AEA-C7D7-4DEB-BF96-FA1789E225E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{EBB55AEA-C7D7-4DEB-BF96-FA1789E225E9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{EBB55AEA-C7D7-4DEB-BF96-FA1789E225E9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{EBB55AEA-C7D7-4DEB-BF96-FA1789E225E9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{AF58C1D5-DE16-429B-B155-7CE83B4E8FA6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{AF58C1D5-DE16-429B-B155-7CE83B4E8FA6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{AF58C1D5-DE16-429B-B155-7CE83B4E8FA6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{AF58C1D5-DE16-429B-B155-7CE83B4E8FA6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{D8F72938-78EF-4E8C-BAFE-531C9C3C8F15}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{D8F72938-78EF-4E8C-BAFE-531C9C3C8F15}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{D8F72938-78EF-4E8C-BAFE-531C9C3C8F15}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{D8F72938-78EF-4E8C-BAFE-531C9C3C8F15}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{CB92CFF9-1D62-4D4F-9E88-8130EF61E351}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{CB92CFF9-1D62-4D4F-9E88-8130EF61E351}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{CB92CFF9-1D62-4D4F-9E88-8130EF61E351}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{CB92CFF9-1D62-4D4F-9E88-8130EF61E351}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{3AB294D0-2230-468F-9EB3-BDFCAEAE99A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{3AB294D0-2230-468F-9EB3-BDFCAEAE99A5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{3AB294D0-2230-468F-9EB3-BDFCAEAE99A5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{3AB294D0-2230-468F-9EB3-BDFCAEAE99A5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{5FD4E4F6-8928-4B3C-BE07-28A675C17226}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{5FD4E4F6-8928-4B3C-BE07-28A675C17226}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{5FD4E4F6-8928-4B3C-BE07-28A675C17226}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{5FD4E4F6-8928-4B3C-BE07-28A675C17226}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{ABF09A5E-2D8B-4B6F-A51D-5CE414DDB15A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{ABF09A5E-2D8B-4B6F-A51D-5CE414DDB15A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{ABF09A5E-2D8B-4B6F-A51D-5CE414DDB15A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{ABF09A5E-2D8B-4B6F-A51D-5CE414DDB15A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{ADA7EA87-0D63-4D97-9433-922A2124401F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{ADA7EA87-0D63-4D97-9433-922A2124401F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{ADA7EA87-0D63-4D97-9433-922A2124401F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{ADA7EA87-0D63-4D97-9433-922A2124401F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{A602AE97-91A5-4608-8DF1-EBF4ED7A0B9E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{A602AE97-91A5-4608-8DF1-EBF4ED7A0B9E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{A602AE97-91A5-4608-8DF1-EBF4ED7A0B9E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{A602AE97-91A5-4608-8DF1-EBF4ED7A0B9E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{9558FB96-075D-4219-8FFF-401979DC0B69}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{9558FB96-075D-4219-8FFF-401979DC0B69}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{9558FB96-075D-4219-8FFF-401979DC0B69}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{9558FB96-075D-4219-8FFF-401979DC0B69}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{E1B1AD28-289D-47B7-A106-326972240207}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{E1B1AD28-289D-47B7-A106-326972240207}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{E1B1AD28-289D-47B7-A106-326972240207}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{E1B1AD28-289D-47B7-A106-326972240207}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{03B955CD-AD84-4B93-AAA7-BF17923BBAA5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{03B955CD-AD84-4B93-AAA7-BF17923BBAA5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{03B955CD-AD84-4B93-AAA7-BF17923BBAA5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{03B955CD-AD84-4B93-AAA7-BF17923BBAA5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{85A0FA56-DC01-4A42-8808-70DAC76BD66D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{85A0FA56-DC01-4A42-8808-70DAC76BD66D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{85A0FA56-DC01-4A42-8808-70DAC76BD66D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{85A0FA56-DC01-4A42-8808-70DAC76BD66D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{806ACF6D-90B0-45D0-A1AC-5F220F3B3985}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{806ACF6D-90B0-45D0-A1AC-5F220F3B3985}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{806ACF6D-90B0-45D0-A1AC-5F220F3B3985}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{806ACF6D-90B0-45D0-A1AC-5F220F3B3985}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{A5E6C691-9E22-4263-8F40-42F002CE66BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{A5E6C691-9E22-4263-8F40-42F002CE66BE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{A5E6C691-9E22-4263-8F40-42F002CE66BE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{A5E6C691-9E22-4263-8F40-42F002CE66BE}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{D1CC5322-7325-4F6B-9625-194B30BE1296}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{D1CC5322-7325-4F6B-9625-194B30BE1296}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{D1CC5322-7325-4F6B-9625-194B30BE1296}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{D1CC5322-7325-4F6B-9625-194B30BE1296}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{3DF35E3D-D844-4399-A9A1-A9E923264C17}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{3DF35E3D-D844-4399-A9A1-A9E923264C17}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{3DF35E3D-D844-4399-A9A1-A9E923264C17}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{3DF35E3D-D844-4399-A9A1-A9E923264C17}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{C3002C3C-7B09-4FE7-894A-372EDA22FC6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{C3002C3C-7B09-4FE7-894A-372EDA22FC6E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{C3002C3C-7B09-4FE7-894A-372EDA22FC6E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{C3002C3C-7B09-4FE7-894A-372EDA22FC6E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{C35F1536-7DE5-4F9D-9604-B5B4E1561947}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{C35F1536-7DE5-4F9D-9604-B5B4E1561947}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{C35F1536-7DE5-4F9D-9604-B5B4E1561947}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{C35F1536-7DE5-4F9D-9604-B5B4E1561947}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{B9AECA11-E248-4886-A10B-81B631CAAF29}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{B9AECA11-E248-4886-A10B-81B631CAAF29}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{B9AECA11-E248-4886-A10B-81B631CAAF29}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{B9AECA11-E248-4886-A10B-81B631CAAF29}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{81BB2C11-9408-4EA3-822E-42987AF54429}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{81BB2C11-9408-4EA3-822E-42987AF54429}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{81BB2C11-9408-4EA3-822E-42987AF54429}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{81BB2C11-9408-4EA3-822E-42987AF54429}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{FD4A2C14-8E3D-4957-ABBE-3C38897B3E2D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{FD4A2C14-8E3D-4957-ABBE-3C38897B3E2D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{FD4A2C14-8E3D-4957-ABBE-3C38897B3E2D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{FD4A2C14-8E3D-4957-ABBE-3C38897B3E2D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{0BE11899-DF2D-4BDE-B9EE-2489E8D35E7D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{0BE11899-DF2D-4BDE-B9EE-2489E8D35E7D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{0BE11899-DF2D-4BDE-B9EE-2489E8D35E7D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{0BE11899-DF2D-4BDE-B9EE-2489E8D35E7D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{716364DE-B988-41A6-BAB4-327964266ECC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{716364DE-B988-41A6-BAB4-327964266ECC}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{716364DE-B988-41A6-BAB4-327964266ECC}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{716364DE-B988-41A6-BAB4-327964266ECC}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{C16F112F-38C3-40BC-9F5F-4791112063D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{C16F112F-38C3-40BC-9F5F-4791112063D6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{C16F112F-38C3-40BC-9F5F-4791112063D6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{C16F112F-38C3-40BC-9F5F-4791112063D6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{DFAB6F2D-B9BF-4AFF-B22B-7684A328EBA3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{DFAB6F2D-B9BF-4AFF-B22B-7684A328EBA3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{DFAB6F2D-B9BF-4AFF-B22B-7684A328EBA3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{DFAB6F2D-B9BF-4AFF-B22B-7684A328EBA3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{2D5D3A1D-5730-4648-B0AB-06C53CB910C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{2D5D3A1D-5730-4648-B0AB-06C53CB910C0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{2D5D3A1D-5730-4648-B0AB-06C53CB910C0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{2D5D3A1D-5730-4648-B0AB-06C53CB910C0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{D99A395A-8569-4DB0-B336-900647890052}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{D99A395A-8569-4DB0-B336-900647890052}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{D99A395A-8569-4DB0-B336-900647890052}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{D99A395A-8569-4DB0-B336-900647890052}.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.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.Build.0 = Release|Any CPU
|
||||||
|
{7C1B2721-13DA-4B62-B046-C626605ECCE6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{7C1B2721-13DA-4B62-B046-C626605ECCE6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{7C1B2721-13DA-4B62-B046-C626605ECCE6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{7C1B2721-13DA-4B62-B046-C626605ECCE6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{BA161CA0-CD65-4E6E-B644-51C8D1E542DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{BA161CA0-CD65-4E6E-B644-51C8D1E542DC}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{BA161CA0-CD65-4E6E-B644-51C8D1E542DC}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{BA161CA0-CD65-4E6E-B644-51C8D1E542DC}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{6AE2A5E8-4C5A-48B9-997B-E1455C0355C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{6AE2A5E8-4C5A-48B9-997B-E1455C0355C6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{6AE2A5E8-4C5A-48B9-997B-E1455C0355C6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{6AE2A5E8-4C5A-48B9-997B-E1455C0355C6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{D4D09B08-D580-4D69-B886-C35D2853F6C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{D4D09B08-D580-4D69-B886-C35D2853F6C8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{D4D09B08-D580-4D69-B886-C35D2853F6C8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{D4D09B08-D580-4D69-B886-C35D2853F6C8}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{2BCB3D7A-38C0-4FE7-8FDA-374C6AD56D0E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{2BCB3D7A-38C0-4FE7-8FDA-374C6AD56D0E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{2BCB3D7A-38C0-4FE7-8FDA-374C6AD56D0E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{2BCB3D7A-38C0-4FE7-8FDA-374C6AD56D0E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{77D01AD9-2C98-478E-AE1D-8F7100738FB4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{77D01AD9-2C98-478E-AE1D-8F7100738FB4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{77D01AD9-2C98-478E-AE1D-8F7100738FB4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{77D01AD9-2C98-478E-AE1D-8F7100738FB4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{77F96ECE-4952-42DB-A528-DED25572A573}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{77F96ECE-4952-42DB-A528-DED25572A573}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{77F96ECE-4952-42DB-A528-DED25572A573}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{77F96ECE-4952-42DB-A528-DED25572A573}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{AF34127A-3A92-43E5-8496-14960A50B1F1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{AF34127A-3A92-43E5-8496-14960A50B1F1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{AF34127A-3A92-43E5-8496-14960A50B1F1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{AF34127A-3A92-43E5-8496-14960A50B1F1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {110169B3-3328-4730-8AB0-BA05BEF75C1A}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
+1
-1
@@ -6,7 +6,7 @@
|
|||||||
<File Path="nuget.config" />
|
<File Path="nuget.config" />
|
||||||
</Folder>
|
</Folder>
|
||||||
<Project Path="src/ARMeilleure/ARMeilleure.csproj" />
|
<Project Path="src/ARMeilleure/ARMeilleure.csproj" />
|
||||||
<Project Path="src/Ryujinx.Audio.Backends.Apple/Ryujinx.Audio.Backends.Apple.csproj" />
|
<Project Path="src/LibKenjinx/LibKenjinx.csproj" />
|
||||||
<Project Path="src/Ryujinx.Audio.Backends.OpenAL/Ryujinx.Audio.Backends.OpenAL.csproj" />
|
<Project Path="src/Ryujinx.Audio.Backends.OpenAL/Ryujinx.Audio.Backends.OpenAL.csproj" />
|
||||||
<Project Path="src/Ryujinx.Audio.Backends.SDL3/Ryujinx.Audio.Backends.SDL3.csproj" />
|
<Project Path="src/Ryujinx.Audio.Backends.SDL3/Ryujinx.Audio.Backends.SDL3.csproj" />
|
||||||
<Project Path="src/Ryujinx.Audio.Backends.SoundIo/Ryujinx.Audio.Backends.SoundIo.csproj" />
|
<Project Path="src/Ryujinx.Audio.Backends.SoundIo/Ryujinx.Audio.Backends.SoundIo.csproj" />
|
||||||
|
|||||||
@@ -2,6 +2,10 @@
|
|||||||
|
|
||||||
SCRIPT_DIR=$(dirname "$(realpath "$0")")
|
SCRIPT_DIR=$(dirname "$(realpath "$0")")
|
||||||
|
|
||||||
|
if [ -f "$SCRIPT_DIR/Ryujinx.Headless.SDL2" ]; then
|
||||||
|
RYUJINX_BIN="Ryujinx.Headless.SDL2"
|
||||||
|
fi
|
||||||
|
|
||||||
if [ -f "$SCRIPT_DIR/Ryujinx" ]; then
|
if [ -f "$SCRIPT_DIR/Ryujinx" ]; then
|
||||||
RYUJINX_BIN="Ryujinx"
|
RYUJINX_BIN="Ryujinx"
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
CURRENTDIR="$(readlink -f "$(dirname "$0")")"
|
|
||||||
exec "$CURRENTDIR"/usr/bin/Ryujinx.sh "$@"
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
set -eu
|
|
||||||
|
|
||||||
ROOTDIR="$(readlink -f "$(dirname "$0")")"/../../../
|
|
||||||
cd "$ROOTDIR"
|
|
||||||
|
|
||||||
BUILDDIR=${BUILDDIR:-publish}
|
|
||||||
OUTDIR=${OUTDIR:-publish_appimage}
|
|
||||||
|
|
||||||
rm -rf AppDir
|
|
||||||
mkdir -p AppDir/usr/bin
|
|
||||||
|
|
||||||
cp distribution/linux/Ryujinx.desktop AppDir/Ryujinx.desktop
|
|
||||||
cp distribution/linux/appimage/AppRun AppDir/AppRun
|
|
||||||
cp src/Ryujinx.UI.Common/Resources/Logo_Ryujinx.png AppDir/Ryujinx.svg
|
|
||||||
|
|
||||||
|
|
||||||
cp -r "$BUILDDIR"/* AppDir/usr/bin/
|
|
||||||
|
|
||||||
# Ensure necessary bins are set as executable
|
|
||||||
chmod +x AppDir/AppRun AppDir/usr/bin/Ryujinx*
|
|
||||||
|
|
||||||
mkdir -p "$OUTDIR"
|
|
||||||
|
|
||||||
appimagetool -n --comp zstd --mksquashfs-opt -Xcompression-level --mksquashfs-opt 21 \
|
|
||||||
AppDir "$OUTDIR"/Ryujinx.AppImage
|
|
||||||
Binary file not shown.
@@ -10,8 +10,6 @@
|
|||||||
<string>Ryujinx</string>
|
<string>Ryujinx</string>
|
||||||
<key>CFBundleIconFile</key>
|
<key>CFBundleIconFile</key>
|
||||||
<string>Ryujinx.icns</string>
|
<string>Ryujinx.icns</string>
|
||||||
<key>CFBundleIconName</key>
|
|
||||||
<string>Ryujinx</string>
|
|
||||||
<key>CFBundleDocumentTypes</key>
|
<key>CFBundleDocumentTypes</key>
|
||||||
<array>
|
<array>
|
||||||
<dict>
|
<dict>
|
||||||
@@ -42,17 +40,17 @@
|
|||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>2.0</string>
|
<string>1.1</string>
|
||||||
<key>CFBundleSignature</key>
|
<key>CFBundleSignature</key>
|
||||||
<string>????</string>
|
<string>????</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>2.0.0</string>
|
<string>1.1.0</string>
|
||||||
<key>NSHighResolutionCapable</key>
|
<key>NSHighResolutionCapable</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>CSResourcesFileMapped</key>
|
<key>CSResourcesFileMapped</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>NSHumanReadableCopyright</key>
|
<key>NSHumanReadableCopyright</key>
|
||||||
<string>Copyright © 2018 - 2024 Ryujinx Team and Contributors. Copyright © 2024 - 2025 Kenji-NX and Contributors.</string>
|
<string>Copyright © 2018 - 2023 Ryujinx Team and Contributors.</string>
|
||||||
<key>LSApplicationCategoryType</key>
|
<key>LSApplicationCategoryType</key>
|
||||||
<string>public.app-category.games</string>
|
<string>public.app-category.games</string>
|
||||||
<key>LSMinimumSystemVersion</key>
|
<key>LSMinimumSystemVersion</key>
|
||||||
|
|||||||
Binary file not shown.
@@ -19,7 +19,7 @@ if platform.system() == "Darwin":
|
|||||||
else:
|
else:
|
||||||
OTOOL = shutil.which("llvm-otool")
|
OTOOL = shutil.which("llvm-otool")
|
||||||
if OTOOL is None:
|
if OTOOL is None:
|
||||||
for llvm_ver in [17, 16, 15, 14, 13]:
|
for llvm_ver in [15, 14, 13]:
|
||||||
otool_path = shutil.which(f"llvm-otool-{llvm_ver}")
|
otool_path = shutil.which(f"llvm-otool-{llvm_ver}")
|
||||||
if otool_path is not None:
|
if otool_path is not None:
|
||||||
OTOOL = otool_path
|
OTOOL = otool_path
|
||||||
@@ -562,29 +562,26 @@ search_path = [
|
|||||||
|
|
||||||
|
|
||||||
for path in content_directory.rglob("**/*.dylib"):
|
for path in content_directory.rglob("**/*.dylib"):
|
||||||
if not path.name.startswith("._"):
|
current_search_path = [path.parent]
|
||||||
current_search_path = [path.parent]
|
current_search_path.extend(search_path)
|
||||||
current_search_path.extend(search_path)
|
|
||||||
|
|
||||||
print(f"Fixing path '{path}' using search path of '{current_search_path}' for context of '{content_directory}'.")
|
fixup_dylib(
|
||||||
fixup_dylib(
|
path,
|
||||||
path,
|
get_path_related_to_target_exec(content_directory, path),
|
||||||
get_path_related_to_target_exec(content_directory, path),
|
current_search_path,
|
||||||
current_search_path,
|
content_directory,
|
||||||
content_directory,
|
)
|
||||||
)
|
|
||||||
|
|
||||||
for path in content_directory.rglob("**/*.so"):
|
for path in content_directory.rglob("**/*.so"):
|
||||||
if not path.name.startswith("._"):
|
current_search_path = [path.parent]
|
||||||
current_search_path = [path.parent]
|
current_search_path.extend(search_path)
|
||||||
current_search_path.extend(search_path)
|
|
||||||
|
|
||||||
fixup_dylib(
|
fixup_dylib(
|
||||||
path,
|
path,
|
||||||
get_path_related_to_target_exec(content_directory, path),
|
get_path_related_to_target_exec(content_directory, path),
|
||||||
current_search_path,
|
current_search_path,
|
||||||
content_directory,
|
content_directory,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
with open(executable_path, "rb") as input:
|
with open(executable_path, "rb") as input:
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ else:
|
|||||||
LIPO = shutil.which("llvm-lipo")
|
LIPO = shutil.which("llvm-lipo")
|
||||||
|
|
||||||
if LIPO is None:
|
if LIPO is None:
|
||||||
for llvm_ver in [17, 16, 15, 14, 13]:
|
for llvm_ver in [15, 14, 13]:
|
||||||
lipo_path = shutil.which(f"llvm-lipo-{llvm_ver}")
|
lipo_path = shutil.which(f"llvm-lipo-{llvm_ver}")
|
||||||
if lipo_path is not None:
|
if lipo_path is not None:
|
||||||
LIPO = lipo_path
|
LIPO = lipo_path
|
||||||
@@ -47,12 +47,14 @@ def get_new_name(
|
|||||||
input_component = str(input_dylib_path).replace(str(input_directory), "")[1:]
|
input_component = str(input_dylib_path).replace(str(input_directory), "")[1:]
|
||||||
return Path(os.path.join(output_directory, input_component))
|
return Path(os.path.join(output_directory, input_component))
|
||||||
|
|
||||||
def get_archs(dylib_path: Path) -> list[str]:
|
|
||||||
res = subprocess.check_output([LIPO, "-info", str(dylib_path)]).decode("utf-8")
|
def is_fat_file(dylib_path: Path) -> str:
|
||||||
if res.startswith("Non-fat file"):
|
res = subprocess.check_output([LIPO, "-info", str(dylib_path.absolute())]).decode(
|
||||||
return [res.split(":")[-1].strip()]
|
"utf-8"
|
||||||
else:
|
)
|
||||||
return res.split("are:")[-1].strip().split()
|
|
||||||
|
return not res.split("\n")[0].startswith("Non-fat file")
|
||||||
|
|
||||||
|
|
||||||
def construct_universal_dylib(
|
def construct_universal_dylib(
|
||||||
arm64_input_dylib_path: Path, x86_64_input_dylib_path: Path, output_dylib_path: Path
|
arm64_input_dylib_path: Path, x86_64_input_dylib_path: Path, output_dylib_path: Path
|
||||||
@@ -67,12 +69,11 @@ def construct_universal_dylib(
|
|||||||
os.path.basename(arm64_input_dylib_path.resolve()), output_dylib_path
|
os.path.basename(arm64_input_dylib_path.resolve()), output_dylib_path
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
arm64_archs = get_archs(arm64_input_dylib_path)
|
if is_fat_file(arm64_input_dylib_path) or not x86_64_input_dylib_path.exists():
|
||||||
x86_64_archs = get_archs(x86_64_input_dylib_path) if x86_64_input_dylib_path.exists() else []
|
with open(output_dylib_path, "wb") as dst:
|
||||||
|
with open(arm64_input_dylib_path, "rb") as src:
|
||||||
if "arm64" in arm64_archs and "x86_64" in arm64_archs:
|
dst.write(src.read())
|
||||||
shutil.copy2(arm64_input_dylib_path, output_dylib_path)
|
else:
|
||||||
elif x86_64_archs:
|
|
||||||
subprocess.check_call(
|
subprocess.check_call(
|
||||||
[
|
[
|
||||||
LIPO,
|
LIPO,
|
||||||
|
|||||||
@@ -25,41 +25,26 @@ cp "$PUBLISH_DIRECTORY"/*.dylib "$APP_BUNDLE_DIRECTORY/Contents/Frameworks"
|
|||||||
cp Info.plist "$APP_BUNDLE_DIRECTORY/Contents"
|
cp Info.plist "$APP_BUNDLE_DIRECTORY/Contents"
|
||||||
cp Ryujinx.icns "$APP_BUNDLE_DIRECTORY/Contents/Resources/Ryujinx.icns"
|
cp Ryujinx.icns "$APP_BUNDLE_DIRECTORY/Contents/Resources/Ryujinx.icns"
|
||||||
cp updater.sh "$APP_BUNDLE_DIRECTORY/Contents/Resources/updater.sh"
|
cp updater.sh "$APP_BUNDLE_DIRECTORY/Contents/Resources/updater.sh"
|
||||||
cp Assets.car "$APP_BUNDLE_DIRECTORY/Contents/Resources/Assets.car"
|
|
||||||
cp -r "$PUBLISH_DIRECTORY/THIRDPARTY.md" "$APP_BUNDLE_DIRECTORY/Contents/Resources"
|
cp -r "$PUBLISH_DIRECTORY/THIRDPARTY.md" "$APP_BUNDLE_DIRECTORY/Contents/Resources"
|
||||||
|
|
||||||
echo -n "APPL????" > "$APP_BUNDLE_DIRECTORY/Contents/PkgInfo"
|
echo -n "APPL????" > "$APP_BUNDLE_DIRECTORY/Contents/PkgInfo"
|
||||||
|
|
||||||
# Fixup libraries and executable
|
# Fixup libraries and executable
|
||||||
echo "Running bundle fix up python script"
|
|
||||||
python3 bundle_fix_up.py "$APP_BUNDLE_DIRECTORY" MacOS/Ryujinx
|
python3 bundle_fix_up.py "$APP_BUNDLE_DIRECTORY" MacOS/Ryujinx
|
||||||
|
|
||||||
# Resign all dyplib files as ad-hoc after changing them
|
|
||||||
find "$APP_BUNDLE_DIRECTORY/Contents/Frameworks" -type f -name "*.dylib" -exec codesign --force --sign - {} \;
|
|
||||||
|
|
||||||
# Now sign it
|
# Now sign it
|
||||||
echo "Starting signing process"
|
|
||||||
if ! [ -x "$(command -v codesign)" ];
|
if ! [ -x "$(command -v codesign)" ];
|
||||||
then
|
then
|
||||||
if ! [ -x "$(command -v rcodesign)" ];
|
if ! [ -x "$(command -v rcodesign)" ];
|
||||||
then
|
then
|
||||||
echo "Cannot find rcodesign on your system, please install rcodesign and ensure it is in your search path."
|
echo "Cannot find rcodesign on your system, please install rcodesign."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Using rcodesign for ad-hoc signing"
|
# cargo install apple-codesign
|
||||||
|
echo "Usign rcodesign for ad-hoc signing"
|
||||||
echo "Resigning all frameworks dylib files as ad-hoc"
|
|
||||||
find "$APP_BUNDLE_DIRECTORY/Contents/Frameworks" -type f -name "*.dylib" -exec rcodesign sign {} \;
|
|
||||||
|
|
||||||
echo "Signing app bundle as ad-hoc"
|
|
||||||
rcodesign sign --entitlements-xml-path "$ENTITLEMENTS_FILE_PATH" "$APP_BUNDLE_DIRECTORY"
|
rcodesign sign --entitlements-xml-path "$ENTITLEMENTS_FILE_PATH" "$APP_BUNDLE_DIRECTORY"
|
||||||
else
|
else
|
||||||
echo "Using codesign for ad-hoc signing"
|
echo "Usign codesign for ad-hoc signing"
|
||||||
|
|
||||||
echo "Resigning all frameworks dylib files as ad-hoc"
|
|
||||||
find "$APP_BUNDLE_DIRECTORY/Contents/Frameworks" -type f -name "*.dylib" -exec codesign --force --sign - {} \;
|
|
||||||
|
|
||||||
echo "Signing app bundle as ad-hoc"
|
|
||||||
codesign --entitlements "$ENTITLEMENTS_FILE_PATH" -f --deep -s - "$APP_BUNDLE_DIRECTORY"
|
codesign --entitlements "$ENTITLEMENTS_FILE_PATH" -f --deep -s - "$APP_BUNDLE_DIRECTORY"
|
||||||
fi
|
fi
|
||||||
@@ -20,19 +20,7 @@ SOURCE_REVISION_ID=$6
|
|||||||
CONFIGURATION=$7
|
CONFIGURATION=$7
|
||||||
EXTRA_ARGS=$8
|
EXTRA_ARGS=$8
|
||||||
|
|
||||||
if [[ "$(uname)" == "Darwin" ]]; then
|
if [ "$VERSION" == "1.1.0" ];
|
||||||
echo "Clearing xattr on all dot underscore files"
|
|
||||||
find "$BASE_DIR" -type f -name "._*" -exec sh -c '
|
|
||||||
for f; do
|
|
||||||
dir=$(dirname "$f")
|
|
||||||
base=$(basename "$f")
|
|
||||||
orig="$dir/${base#._}"
|
|
||||||
[ -f "$orig" ] && xattr -c "$orig" || true
|
|
||||||
done
|
|
||||||
' sh {} +
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$VERSION" == "2.0.0" ];
|
|
||||||
then
|
then
|
||||||
RELEASE_TAR_FILE_NAME=ryujinx-$CONFIGURATION-$VERSION+$SOURCE_REVISION_ID-macos_universal.app.tar
|
RELEASE_TAR_FILE_NAME=ryujinx-$CONFIGURATION-$VERSION+$SOURCE_REVISION_ID-macos_universal.app.tar
|
||||||
else
|
else
|
||||||
@@ -78,11 +66,11 @@ python3 "$BASE_DIR/distribution/macos/construct_universal_dylib.py" "$ARM64_APP_
|
|||||||
|
|
||||||
if ! [ -x "$(command -v lipo)" ];
|
if ! [ -x "$(command -v lipo)" ];
|
||||||
then
|
then
|
||||||
if ! [ -x "$(command -v llvm-lipo-17)" ];
|
if ! [ -x "$(command -v llvm-lipo-14)" ];
|
||||||
then
|
then
|
||||||
LIPO=llvm-lipo
|
LIPO=llvm-lipo
|
||||||
else
|
else
|
||||||
LIPO=llvm-lipo-17
|
LIPO=llvm-lipo-14
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
LIPO=lipo
|
LIPO=lipo
|
||||||
@@ -121,6 +109,12 @@ python3 "$BASE_DIR/distribution/misc/add_tar_exec.py" "$RELEASE_TAR_FILE_NAME" "
|
|||||||
gzip -9 < "$RELEASE_TAR_FILE_NAME" > "$RELEASE_TAR_FILE_NAME.gz"
|
gzip -9 < "$RELEASE_TAR_FILE_NAME" > "$RELEASE_TAR_FILE_NAME.gz"
|
||||||
rm "$RELEASE_TAR_FILE_NAME"
|
rm "$RELEASE_TAR_FILE_NAME"
|
||||||
|
|
||||||
|
# Create legacy update package for Avalonia to not left behind old testers.
|
||||||
|
if [ "$VERSION" != "1.1.0" ];
|
||||||
|
then
|
||||||
|
cp $RELEASE_TAR_FILE_NAME.gz test-ava-ryujinx-$VERSION-macos_universal.app.tar.gz
|
||||||
|
fi
|
||||||
|
|
||||||
popd
|
popd
|
||||||
|
|
||||||
echo "Done"
|
echo "Done"
|
||||||
+111
@@ -0,0 +1,111 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
if [ "$#" -lt 7 ]; then
|
||||||
|
echo "usage <BASE_DIR> <TEMP_DIRECTORY> <OUTPUT_DIRECTORY> <ENTITLEMENTS_FILE_PATH> <VERSION> <SOURCE_REVISION_ID> <CONFIGURATION> <EXTRA_ARGS>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$1"
|
||||||
|
mkdir -p "$2"
|
||||||
|
mkdir -p "$3"
|
||||||
|
|
||||||
|
BASE_DIR=$(readlink -f "$1")
|
||||||
|
TEMP_DIRECTORY=$(readlink -f "$2")
|
||||||
|
OUTPUT_DIRECTORY=$(readlink -f "$3")
|
||||||
|
ENTITLEMENTS_FILE_PATH=$(readlink -f "$4")
|
||||||
|
VERSION=$5
|
||||||
|
SOURCE_REVISION_ID=$6
|
||||||
|
CONFIGURATION=$7
|
||||||
|
EXTRA_ARGS=$8
|
||||||
|
|
||||||
|
if [ "$VERSION" == "1.1.0" ];
|
||||||
|
then
|
||||||
|
RELEASE_TAR_FILE_NAME=sdl2-ryujinx-headless-$CONFIGURATION-$VERSION+$SOURCE_REVISION_ID-macos_universal.tar
|
||||||
|
else
|
||||||
|
RELEASE_TAR_FILE_NAME=sdl2-ryujinx-headless-$VERSION-macos_universal.tar
|
||||||
|
fi
|
||||||
|
|
||||||
|
ARM64_OUTPUT="$TEMP_DIRECTORY/publish_arm64"
|
||||||
|
X64_OUTPUT="$TEMP_DIRECTORY/publish_x64"
|
||||||
|
UNIVERSAL_OUTPUT="$OUTPUT_DIRECTORY/publish"
|
||||||
|
EXECUTABLE_SUB_PATH=Ryujinx.Headless.SDL2
|
||||||
|
|
||||||
|
rm -rf "$TEMP_DIRECTORY"
|
||||||
|
mkdir -p "$TEMP_DIRECTORY"
|
||||||
|
|
||||||
|
DOTNET_COMMON_ARGS=(-p:DebugType=embedded -p:Version="$VERSION" -p:SourceRevisionId="$SOURCE_REVISION_ID" --self-contained true $EXTRA_ARGS)
|
||||||
|
|
||||||
|
dotnet restore
|
||||||
|
dotnet build -c "$CONFIGURATION" src/Ryujinx.Headless.SDL2
|
||||||
|
dotnet publish -c "$CONFIGURATION" -r osx-arm64 -o "$TEMP_DIRECTORY/publish_arm64" "${DOTNET_COMMON_ARGS[@]}" src/Ryujinx.Headless.SDL2
|
||||||
|
dotnet publish -c "$CONFIGURATION" -r osx-x64 -o "$TEMP_DIRECTORY/publish_x64" "${DOTNET_COMMON_ARGS[@]}" src/Ryujinx.Headless.SDL2
|
||||||
|
|
||||||
|
# Get rid of the support library for ARMeilleure for x64 (that's only for arm64)
|
||||||
|
rm -rf "$TEMP_DIRECTORY/publish_x64/libarmeilleure-jitsupport.dylib"
|
||||||
|
|
||||||
|
# Get rid of libsoundio from arm64 builds as we don't have a arm64 variant
|
||||||
|
# TODO: remove this once done
|
||||||
|
rm -rf "$TEMP_DIRECTORY/publish_arm64/libsoundio.dylib"
|
||||||
|
|
||||||
|
rm -rf "$OUTPUT_DIRECTORY"
|
||||||
|
mkdir -p "$OUTPUT_DIRECTORY"
|
||||||
|
|
||||||
|
# Let's copy one of the two different outputs and remove the executable
|
||||||
|
cp -R "$ARM64_OUTPUT/" "$UNIVERSAL_OUTPUT"
|
||||||
|
rm "$UNIVERSAL_OUTPUT/$EXECUTABLE_SUB_PATH"
|
||||||
|
|
||||||
|
# Make it libraries universal
|
||||||
|
python3 "$BASE_DIR/distribution/macos/construct_universal_dylib.py" "$ARM64_OUTPUT" "$X64_OUTPUT" "$UNIVERSAL_OUTPUT" "**/*.dylib"
|
||||||
|
|
||||||
|
if ! [ -x "$(command -v lipo)" ];
|
||||||
|
then
|
||||||
|
if ! [ -x "$(command -v llvm-lipo-14)" ];
|
||||||
|
then
|
||||||
|
LIPO=llvm-lipo
|
||||||
|
else
|
||||||
|
LIPO=llvm-lipo-14
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
LIPO=lipo
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Make the executable universal
|
||||||
|
$LIPO "$ARM64_OUTPUT/$EXECUTABLE_SUB_PATH" "$X64_OUTPUT/$EXECUTABLE_SUB_PATH" -output "$UNIVERSAL_OUTPUT/$EXECUTABLE_SUB_PATH" -create
|
||||||
|
|
||||||
|
# Now sign it
|
||||||
|
if ! [ -x "$(command -v codesign)" ];
|
||||||
|
then
|
||||||
|
if ! [ -x "$(command -v rcodesign)" ];
|
||||||
|
then
|
||||||
|
echo "Cannot find rcodesign on your system, please install rcodesign."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# NOTE: Currently require https://github.com/indygreg/apple-platform-rs/pull/44 to work on other OSes.
|
||||||
|
# cargo install --git "https://github.com/marysaka/apple-platform-rs" --branch "fix/adhoc-app-bundle" apple-codesign --bin "rcodesign"
|
||||||
|
echo "Using rcodesign for ad-hoc signing"
|
||||||
|
for FILE in "$UNIVERSAL_OUTPUT"/*; do
|
||||||
|
if [[ $(file "$FILE") == *"Mach-O"* ]]; then
|
||||||
|
rcodesign sign --entitlements-xml-path "$ENTITLEMENTS_FILE_PATH" "$FILE"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
else
|
||||||
|
echo "Using codesign for ad-hoc signing"
|
||||||
|
for FILE in "$UNIVERSAL_OUTPUT"/*; do
|
||||||
|
if [[ $(file "$FILE") == *"Mach-O"* ]]; then
|
||||||
|
codesign --entitlements "$ENTITLEMENTS_FILE_PATH" -f --deep -s - "$FILE"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Creating archive"
|
||||||
|
pushd "$OUTPUT_DIRECTORY"
|
||||||
|
tar --exclude "publish/Ryujinx.Headless.SDL2" -cvf "$RELEASE_TAR_FILE_NAME" publish 1> /dev/null
|
||||||
|
python3 "$BASE_DIR/distribution/misc/add_tar_exec.py" "$RELEASE_TAR_FILE_NAME" "publish/Ryujinx.Headless.SDL2" "publish/Ryujinx.Headless.SDL2"
|
||||||
|
gzip -9 < "$RELEASE_TAR_FILE_NAME" > "$RELEASE_TAR_FILE_NAME.gz"
|
||||||
|
rm "$RELEASE_TAR_FILE_NAME"
|
||||||
|
popd
|
||||||
|
|
||||||
|
echo "Done"
|
||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"sdk": {
|
"sdk": {
|
||||||
"version": "10.0.100",
|
"version": "9.0.100",
|
||||||
"rollForward": "latestFeature"
|
"rollForward": "latestFeature"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ namespace ARMeilleure.CodeGen
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a compiled function.
|
/// Represents a compiled function.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly struct CompiledFunction
|
readonly struct CompiledFunction
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the machine code of the <see cref="CompiledFunction"/>.
|
/// Gets the machine code of the <see cref="CompiledFunction"/>.
|
||||||
@@ -44,11 +44,10 @@ namespace ARMeilleure.CodeGen
|
|||||||
/// <typeparamref name="T"/> pointing to the mapped function.
|
/// <typeparamref name="T"/> pointing to the mapped function.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">Type of delegate</typeparam>
|
/// <typeparam name="T">Type of delegate</typeparam>
|
||||||
/// <param name="jitCache">The jit cache to map the function into</param>
|
|
||||||
/// <returns>A delegate of type <typeparamref name="T"/> pointing to the mapped function</returns>
|
/// <returns>A delegate of type <typeparamref name="T"/> pointing to the mapped function</returns>
|
||||||
public T Map<T>(JitCache jitCache)
|
public T Map<T>()
|
||||||
{
|
{
|
||||||
return MapWithPointer<T>(jitCache, out _);
|
return MapWithPointer<T>(out _);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -56,12 +55,11 @@ namespace ARMeilleure.CodeGen
|
|||||||
/// <typeparamref name="T"/> pointing to the mapped function.
|
/// <typeparamref name="T"/> pointing to the mapped function.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">Type of delegate</typeparam>
|
/// <typeparam name="T">Type of delegate</typeparam>
|
||||||
/// <param name="jitCache">The jit cache to map the function into</param>
|
|
||||||
/// <param name="codePointer">Pointer to the function code in memory</param>
|
/// <param name="codePointer">Pointer to the function code in memory</param>
|
||||||
/// <returns>A delegate of type <typeparamref name="T"/> pointing to the mapped function</returns>
|
/// <returns>A delegate of type <typeparamref name="T"/> pointing to the mapped function</returns>
|
||||||
public T MapWithPointer<T>(JitCache jitCache, out nint codePointer)
|
public T MapWithPointer<T>(out nint codePointer)
|
||||||
{
|
{
|
||||||
codePointer = jitCache.Map(this);
|
codePointer = JitCache.Map(this);
|
||||||
|
|
||||||
return Marshal.GetDelegateForFunctionPointer<T>(codePointer);
|
return Marshal.GetDelegateForFunctionPointer<T>(codePointer);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ namespace ARMeilleure.CodeGen.Linking
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a relocation.
|
/// Represents a relocation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly struct RelocEntry
|
readonly struct RelocEntry
|
||||||
{
|
{
|
||||||
public const int Stride = 13; // Bytes.
|
public const int Stride = 13; // Bytes.
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ namespace ARMeilleure.CodeGen.Linking
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents relocation information about a <see cref="CompiledFunction"/>.
|
/// Represents relocation information about a <see cref="CompiledFunction"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly struct RelocInfo
|
readonly struct RelocInfo
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets an empty <see cref="RelocInfo"/>.
|
/// Gets an empty <see cref="RelocInfo"/>.
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ namespace ARMeilleure.CodeGen.Linking
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a symbol.
|
/// Represents a symbol.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly struct Symbol
|
readonly struct Symbol
|
||||||
{
|
{
|
||||||
private readonly ulong _value;
|
private readonly ulong _value;
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ namespace ARMeilleure.CodeGen.Linking
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Types of <see cref="Symbol"/>.
|
/// Types of <see cref="Symbol"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public enum SymbolType : byte
|
enum SymbolType : byte
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Refers to nothing, i.e no symbol.
|
/// Refers to nothing, i.e no symbol.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
namespace ARMeilleure.CodeGen.Unwinding
|
namespace ARMeilleure.CodeGen.Unwinding
|
||||||
{
|
{
|
||||||
public struct UnwindInfo
|
struct UnwindInfo
|
||||||
{
|
{
|
||||||
public const int Stride = 4; // Bytes.
|
public const int Stride = 4; // Bytes.
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
namespace ARMeilleure.CodeGen.Unwinding
|
namespace ARMeilleure.CodeGen.Unwinding
|
||||||
{
|
{
|
||||||
public enum UnwindPseudoOp
|
enum UnwindPseudoOp
|
||||||
{
|
{
|
||||||
PushReg = 0,
|
PushReg = 0,
|
||||||
SetFrame = 1,
|
SetFrame = 1,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
namespace ARMeilleure.CodeGen.Unwinding
|
namespace ARMeilleure.CodeGen.Unwinding
|
||||||
{
|
{
|
||||||
public struct UnwindPushEntry
|
struct UnwindPushEntry
|
||||||
{
|
{
|
||||||
public const int Stride = 16; // Bytes.
|
public const int Stride = 16; // Bytes.
|
||||||
|
|
||||||
|
|||||||
@@ -20,21 +20,45 @@ namespace ARMeilleure.Common
|
|||||||
new( 1, 6)
|
new( 1, 6)
|
||||||
];
|
];
|
||||||
|
|
||||||
private static readonly AddressTableLevel[] _levels64BitMono =
|
private static readonly AddressTableLevel[] _levels64BitSparseTiny =
|
||||||
[
|
[
|
||||||
new( 2, 37)
|
new( 11, 28),
|
||||||
|
new( 2, 9)
|
||||||
];
|
];
|
||||||
|
|
||||||
private static readonly AddressTableLevel[] _levels32BitMono =
|
private static readonly AddressTableLevel[] _levels32BitSparseTiny =
|
||||||
[
|
[
|
||||||
new( 1, 31)
|
new( 10, 22),
|
||||||
|
new( 1, 9)
|
||||||
];
|
];
|
||||||
|
|
||||||
public static AddressTableLevel[] GetArmPreset(bool for64Bits, bool mono)
|
private static readonly AddressTableLevel[] _levels64BitSparseGiant =
|
||||||
|
[
|
||||||
|
new( 38, 1),
|
||||||
|
new( 2, 36)
|
||||||
|
];
|
||||||
|
|
||||||
|
private static readonly AddressTableLevel[] _levels32BitSparseGiant =
|
||||||
|
[
|
||||||
|
new( 31, 1),
|
||||||
|
new( 1, 30)
|
||||||
|
];
|
||||||
|
|
||||||
|
//high power will run worse on DDR3 systems and some DDR4 systems due to the higher ram utilization
|
||||||
|
//low power will never run worse than non-sparse, but for most systems it won't be necessary
|
||||||
|
//high power is always used, but I've left low power in here for future reference
|
||||||
|
public static AddressTableLevel[] GetArmPreset(bool for64Bits, bool sparse, bool lowPower = false)
|
||||||
{
|
{
|
||||||
if (mono)
|
if (sparse)
|
||||||
{
|
{
|
||||||
return for64Bits ? _levels64BitMono : _levels32BitMono;
|
if (lowPower)
|
||||||
|
{
|
||||||
|
return for64Bits ? _levels64BitSparseTiny : _levels32BitSparseTiny;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return for64Bits ? _levels64BitSparseGiant : _levels32BitSparseGiant;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
namespace ARMeilleure.Common
|
|
||||||
{
|
|
||||||
public enum AddressTableType
|
|
||||||
{
|
|
||||||
Default,
|
|
||||||
Sparse
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -53,14 +53,5 @@ namespace ARMeilleure.Common
|
|||||||
{
|
{
|
||||||
return (bits >> shift) | (bits << (size - shift));
|
return (bits >> shift) | (bits << (size - shift));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static T AlignUp<T>(T value, T size) where T : IBinaryInteger<T>
|
|
||||||
=> (value + (size - T.One)) & -size;
|
|
||||||
|
|
||||||
public static T AlignDown<T>(T value, T size) where T : IBinaryInteger<T>
|
|
||||||
=> value & -size;
|
|
||||||
|
|
||||||
public static T DivRoundUp<T>(T value, T dividend) where T : IBinaryInteger<T>
|
|
||||||
=> (value + (dividend - T.One)) / dividend;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,9 +5,10 @@ namespace ARMeilleure.Common
|
|||||||
public interface IAddressTable<TEntry> : IDisposable where TEntry : unmanaged
|
public interface IAddressTable<TEntry> : IDisposable where TEntry : unmanaged
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the <see cref="AddressTableType"/> of the <see cref="IAddressTable{TEntry}"/> instance.
|
/// True if the address table's bottom level is sparsely mapped.
|
||||||
|
/// This also ensures the second bottom level is filled with a dummy page rather than 0.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
AddressTableType TableType { get; }
|
bool Sparse { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the bits used by the <see cref="Levels"/> of the <see cref="IAddressTable{TEntry}"/> instance.
|
/// Gets the bits used by the <see cref="Levels"/> of the <see cref="IAddressTable{TEntry}"/> instance.
|
||||||
@@ -30,13 +31,6 @@ namespace ARMeilleure.Common
|
|||||||
/// <exception cref="ObjectDisposedException"><see cref="EntryTable{TEntry}"/> instance was disposed</exception>
|
/// <exception cref="ObjectDisposedException"><see cref="EntryTable{TEntry}"/> instance was disposed</exception>
|
||||||
nint Base { get; }
|
nint Base { get; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Signal that the given code range exists.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="address">Guest code range address</param>
|
|
||||||
/// <param name="size">Guest code range size</param>
|
|
||||||
void SignalCodeRange(ulong address, ulong size);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines if the specified <paramref name="address"/> is in the range of the
|
/// Determines if the specified <paramref name="address"/> is in the range of the
|
||||||
/// <see cref="IAddressTable{TEntry}"/>.
|
/// <see cref="IAddressTable{TEntry}"/>.
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ using ARMeilleure.IntermediateRepresentation;
|
|||||||
using ARMeilleure.State;
|
using ARMeilleure.State;
|
||||||
using ARMeilleure.Translation;
|
using ARMeilleure.Translation;
|
||||||
using ARMeilleure.Translation.PTC;
|
using ARMeilleure.Translation.PTC;
|
||||||
using System.Linq;
|
|
||||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||||
|
|
||||||
@@ -147,12 +147,6 @@ 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)
|
||||||
@@ -167,24 +161,12 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
public static void EmitVirtualCall(ArmEmitterContext context, Operand target)
|
public static void EmitVirtualCall(ArmEmitterContext context, Operand target)
|
||||||
{
|
{
|
||||||
if (context.IsSingleStep)
|
EmitTableBranch(context, target, isJump: false);
|
||||||
{
|
|
||||||
if (target.Type == OperandType.I32)
|
|
||||||
{
|
|
||||||
target = context.ZeroExtend32(OperandType.I64, target);
|
|
||||||
}
|
|
||||||
|
|
||||||
context.Return(target);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
EmitTableBranch(context, target, isJump: false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void EmitVirtualJump(ArmEmitterContext context, Operand target, bool isReturn)
|
public static void EmitVirtualJump(ArmEmitterContext context, Operand target, bool isReturn)
|
||||||
{
|
{
|
||||||
if (isReturn || context.IsSingleStep)
|
if (isReturn)
|
||||||
{
|
{
|
||||||
EmitReturn(context, target);
|
EmitReturn(context, target);
|
||||||
}
|
}
|
||||||
@@ -236,9 +218,9 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
hostAddress = context.Load(OperandType.I64, hostAddressAddr);
|
hostAddress = context.Load(OperandType.I64, hostAddressAddr);
|
||||||
}
|
}
|
||||||
else if (table.TableType == AddressTableType.Sparse)
|
else if (table.Sparse)
|
||||||
{
|
{
|
||||||
// Inline table lookup. Only enabled when the sparse function table is enabled with 1 level.
|
// Inline table lookup. Only enabled when the sparse function table is enabled with 2 levels.
|
||||||
// Deliberately attempts to avoid branches.
|
// Deliberately attempts to avoid branches.
|
||||||
|
|
||||||
Operand tableBase = !context.HasPtc ?
|
Operand tableBase = !context.HasPtc ?
|
||||||
@@ -247,21 +229,24 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
hostAddress = tableBase;
|
hostAddress = tableBase;
|
||||||
|
|
||||||
AddressTableLevel level = table.Levels.Last();
|
for (int i = 0; i < table.Levels.Length; i++)
|
||||||
int clearBits = 64 - (level.Index + level.Length);
|
{
|
||||||
|
AddressTableLevel level = table.Levels[i];
|
||||||
|
int clearBits = 64 - (level.Index + level.Length);
|
||||||
|
|
||||||
Operand index = context.ShiftLeft(
|
Operand index = context.ShiftLeft(
|
||||||
context.ShiftRightUI(context.ShiftLeft(guestAddress, Const(clearBits)), Const(clearBits + level.Index)),
|
context.ShiftRightUI(context.ShiftLeft(guestAddress, Const(clearBits)), Const(clearBits + level.Index)),
|
||||||
Const(3)
|
Const(3)
|
||||||
);
|
);
|
||||||
|
|
||||||
hostAddress = context.Load(OperandType.I64, context.Add(hostAddress, index));
|
hostAddress = context.Load(OperandType.I64, context.Add(hostAddress, index));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
hostAddress = !context.HasPtc ?
|
hostAddress = !context.HasPtc ?
|
||||||
Const(context.Stubs.DispatchStub) :
|
Const((long)context.Stubs.DispatchStub) :
|
||||||
Const(context.Stubs.DispatchStub, Ptc.DispatchStubSymbol);
|
Const((long)context.Stubs.DispatchStub, Ptc.DispatchStubSymbol);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isJump)
|
if (isJump)
|
||||||
|
|||||||
@@ -1,35 +1,59 @@
|
|||||||
using System;
|
using System;
|
||||||
|
#if ANDROID
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
#else
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace ARMeilleure.Instructions
|
namespace ARMeilleure.Instructions
|
||||||
{
|
{
|
||||||
static class MathHelper
|
static class MathHelper
|
||||||
{
|
{
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static double Abs(double value)
|
public static double Abs(double value)
|
||||||
{
|
{
|
||||||
return Math.Abs(value);
|
return Math.Abs(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static double Ceiling(double value)
|
public static double Ceiling(double value)
|
||||||
{
|
{
|
||||||
return Math.Ceiling(value);
|
return Math.Ceiling(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static double Floor(double value)
|
public static double Floor(double value)
|
||||||
{
|
{
|
||||||
return Math.Floor(value);
|
return Math.Floor(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static double Round(double value, int mode)
|
public static double Round(double value, int mode)
|
||||||
{
|
{
|
||||||
return Math.Round(value, (MidpointRounding)mode);
|
return Math.Round(value, (MidpointRounding)mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static double Truncate(double value)
|
public static double Truncate(double value)
|
||||||
{
|
{
|
||||||
return Math.Truncate(value);
|
return Math.Truncate(value);
|
||||||
@@ -38,31 +62,51 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
static class MathHelperF
|
static class MathHelperF
|
||||||
{
|
{
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static float Abs(float value)
|
public static float Abs(float value)
|
||||||
{
|
{
|
||||||
return MathF.Abs(value);
|
return MathF.Abs(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static float Ceiling(float value)
|
public static float Ceiling(float value)
|
||||||
{
|
{
|
||||||
return MathF.Ceiling(value);
|
return MathF.Ceiling(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static float Floor(float value)
|
public static float Floor(float value)
|
||||||
{
|
{
|
||||||
return MathF.Floor(value);
|
return MathF.Floor(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static float Round(float value, int mode)
|
public static float Round(float value, int mode)
|
||||||
{
|
{
|
||||||
return MathF.Round(value, (MidpointRounding)mode);
|
return MathF.Round(value, (MidpointRounding)mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static float Truncate(float value)
|
public static float Truncate(float value)
|
||||||
{
|
{
|
||||||
return MathF.Truncate(value);
|
return MathF.Truncate(value);
|
||||||
|
|||||||
@@ -2,7 +2,11 @@ using ARMeilleure.Memory;
|
|||||||
using ARMeilleure.State;
|
using ARMeilleure.State;
|
||||||
using ARMeilleure.Translation;
|
using ARMeilleure.Translation;
|
||||||
using System;
|
using System;
|
||||||
|
#if ANDROID
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
#else
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace ARMeilleure.Instructions
|
namespace ARMeilleure.Instructions
|
||||||
{
|
{
|
||||||
@@ -35,7 +39,11 @@ namespace ARMeilleure.Instructions
|
|||||||
Context = null;
|
Context = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static void Break(ulong address, int imm)
|
public static void Break(ulong address, int imm)
|
||||||
{
|
{
|
||||||
Statistics.PauseTimer();
|
Statistics.PauseTimer();
|
||||||
@@ -45,7 +53,11 @@ namespace ARMeilleure.Instructions
|
|||||||
Statistics.ResumeTimer();
|
Statistics.ResumeTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static void SupervisorCall(ulong address, int imm)
|
public static void SupervisorCall(ulong address, int imm)
|
||||||
{
|
{
|
||||||
Statistics.PauseTimer();
|
Statistics.PauseTimer();
|
||||||
@@ -55,7 +67,11 @@ namespace ARMeilleure.Instructions
|
|||||||
Statistics.ResumeTimer();
|
Statistics.ResumeTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static void Undefined(ulong address, int opCode)
|
public static void Undefined(ulong address, int opCode)
|
||||||
{
|
{
|
||||||
Statistics.PauseTimer();
|
Statistics.PauseTimer();
|
||||||
@@ -66,31 +82,51 @@ namespace ARMeilleure.Instructions
|
|||||||
}
|
}
|
||||||
|
|
||||||
#region "System registers"
|
#region "System registers"
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static ulong GetCtrEl0()
|
public static ulong GetCtrEl0()
|
||||||
{
|
{
|
||||||
return GetContext().CtrEl0;
|
return GetContext().CtrEl0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static ulong GetDczidEl0()
|
public static ulong GetDczidEl0()
|
||||||
{
|
{
|
||||||
return GetContext().DczidEl0;
|
return GetContext().DczidEl0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static ulong GetCntfrqEl0()
|
public static ulong GetCntfrqEl0()
|
||||||
{
|
{
|
||||||
return GetContext().CntfrqEl0;
|
return GetContext().CntfrqEl0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static ulong GetCntpctEl0()
|
public static ulong GetCntpctEl0()
|
||||||
{
|
{
|
||||||
return GetContext().CntpctEl0;
|
return GetContext().CntpctEl0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static ulong GetCntvctEl0()
|
public static ulong GetCntvctEl0()
|
||||||
{
|
{
|
||||||
return GetContext().CntvctEl0;
|
return GetContext().CntvctEl0;
|
||||||
@@ -98,31 +134,51 @@ namespace ARMeilleure.Instructions
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region "Read"
|
#region "Read"
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static byte ReadByte(ulong address)
|
public static byte ReadByte(ulong address)
|
||||||
{
|
{
|
||||||
return GetMemoryManager().ReadGuest<byte>(address);
|
return GetMemoryManager().ReadGuest<byte>(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static ushort ReadUInt16(ulong address)
|
public static ushort ReadUInt16(ulong address)
|
||||||
{
|
{
|
||||||
return GetMemoryManager().ReadGuest<ushort>(address);
|
return GetMemoryManager().ReadGuest<ushort>(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static uint ReadUInt32(ulong address)
|
public static uint ReadUInt32(ulong address)
|
||||||
{
|
{
|
||||||
return GetMemoryManager().ReadGuest<uint>(address);
|
return GetMemoryManager().ReadGuest<uint>(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static ulong ReadUInt64(ulong address)
|
public static ulong ReadUInt64(ulong address)
|
||||||
{
|
{
|
||||||
return GetMemoryManager().ReadGuest<ulong>(address);
|
return GetMemoryManager().ReadGuest<ulong>(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static V128 ReadVector128(ulong address)
|
public static V128 ReadVector128(ulong address)
|
||||||
{
|
{
|
||||||
return GetMemoryManager().ReadGuest<V128>(address);
|
return GetMemoryManager().ReadGuest<V128>(address);
|
||||||
@@ -130,56 +186,92 @@ namespace ARMeilleure.Instructions
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region "Write"
|
#region "Write"
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static void WriteByte(ulong address, byte value)
|
public static void WriteByte(ulong address, byte value)
|
||||||
{
|
{
|
||||||
GetMemoryManager().WriteGuest(address, value);
|
GetMemoryManager().WriteGuest(address, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static void WriteUInt16(ulong address, ushort value)
|
public static void WriteUInt16(ulong address, ushort value)
|
||||||
{
|
{
|
||||||
GetMemoryManager().WriteGuest(address, value);
|
GetMemoryManager().WriteGuest(address, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static void WriteUInt32(ulong address, uint value)
|
public static void WriteUInt32(ulong address, uint value)
|
||||||
{
|
{
|
||||||
GetMemoryManager().WriteGuest(address, value);
|
GetMemoryManager().WriteGuest(address, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static void WriteUInt64(ulong address, ulong value)
|
public static void WriteUInt64(ulong address, ulong value)
|
||||||
{
|
{
|
||||||
GetMemoryManager().WriteGuest(address, value);
|
GetMemoryManager().WriteGuest(address, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static void WriteVector128(ulong address, V128 value)
|
public static void WriteVector128(ulong address, V128 value)
|
||||||
{
|
{
|
||||||
GetMemoryManager().WriteGuest(address, value);
|
GetMemoryManager().WriteGuest(address, value);
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static void EnqueueForRejit(ulong address)
|
public static void EnqueueForRejit(ulong address)
|
||||||
{
|
{
|
||||||
Context.Translator.EnqueueForRejit(address, GetContext().ExecutionMode);
|
Context.Translator.EnqueueForRejit(address, GetContext().ExecutionMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static void SignalMemoryTracking(ulong address, ulong size, byte write)
|
public static void SignalMemoryTracking(ulong address, ulong size, byte write)
|
||||||
{
|
{
|
||||||
GetMemoryManager().SignalMemoryTracking(address, size, write == 1);
|
GetMemoryManager().SignalMemoryTracking(address, size, write == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static void ThrowInvalidMemoryAccess(ulong address)
|
public static void ThrowInvalidMemoryAccess(ulong address)
|
||||||
{
|
{
|
||||||
throw new InvalidAccessException(address);
|
throw new InvalidAccessException(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static ulong GetFunctionAddress(ulong address)
|
public static ulong GetFunctionAddress(ulong address)
|
||||||
{
|
{
|
||||||
TranslatedFunction function = Context.Translator.GetOrTranslate(address, GetContext().ExecutionMode);
|
TranslatedFunction function = Context.Translator.GetOrTranslate(address, GetContext().ExecutionMode);
|
||||||
@@ -187,13 +279,21 @@ namespace ARMeilleure.Instructions
|
|||||||
return (ulong)function.FuncPointer.ToInt64();
|
return (ulong)function.FuncPointer.ToInt64();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static void InvalidateCacheLine(ulong address)
|
public static void InvalidateCacheLine(ulong address)
|
||||||
{
|
{
|
||||||
Context.Translator.InvalidateJitCacheRegion(address, InstEmit.DczSizeInBytes);
|
Context.Translator.InvalidateJitCacheRegion(address, InstEmit.DczSizeInBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static byte CheckSynchronization()
|
public static byte CheckSynchronization()
|
||||||
{
|
{
|
||||||
Statistics.PauseTimer();
|
Statistics.PauseTimer();
|
||||||
|
|||||||
@@ -1,13 +1,21 @@
|
|||||||
using ARMeilleure.State;
|
using ARMeilleure.State;
|
||||||
using System;
|
using System;
|
||||||
|
#if ANDROID
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
#else
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace ARMeilleure.Instructions
|
namespace ARMeilleure.Instructions
|
||||||
{
|
{
|
||||||
static class SoftFallback
|
static class SoftFallback
|
||||||
{
|
{
|
||||||
#region "ShrImm64"
|
#region "ShrImm64"
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static long SignedShrImm64(long value, long roundConst, int shift)
|
public static long SignedShrImm64(long value, long roundConst, int shift)
|
||||||
{
|
{
|
||||||
if (roundConst == 0L)
|
if (roundConst == 0L)
|
||||||
@@ -50,7 +58,11 @@ namespace ARMeilleure.Instructions
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static ulong UnsignedShrImm64(ulong value, long roundConst, int shift)
|
public static ulong UnsignedShrImm64(ulong value, long roundConst, int shift)
|
||||||
{
|
{
|
||||||
if (roundConst == 0L)
|
if (roundConst == 0L)
|
||||||
@@ -95,7 +107,11 @@ namespace ARMeilleure.Instructions
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region "Saturation"
|
#region "Saturation"
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static int SatF32ToS32(float value)
|
public static int SatF32ToS32(float value)
|
||||||
{
|
{
|
||||||
if (float.IsNaN(value))
|
if (float.IsNaN(value))
|
||||||
@@ -107,7 +123,11 @@ namespace ARMeilleure.Instructions
|
|||||||
value <= int.MinValue ? int.MinValue : (int)value;
|
value <= int.MinValue ? int.MinValue : (int)value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static long SatF32ToS64(float value)
|
public static long SatF32ToS64(float value)
|
||||||
{
|
{
|
||||||
if (float.IsNaN(value))
|
if (float.IsNaN(value))
|
||||||
@@ -119,7 +139,11 @@ namespace ARMeilleure.Instructions
|
|||||||
value <= long.MinValue ? long.MinValue : (long)value;
|
value <= long.MinValue ? long.MinValue : (long)value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static uint SatF32ToU32(float value)
|
public static uint SatF32ToU32(float value)
|
||||||
{
|
{
|
||||||
if (float.IsNaN(value))
|
if (float.IsNaN(value))
|
||||||
@@ -131,7 +155,11 @@ namespace ARMeilleure.Instructions
|
|||||||
value <= uint.MinValue ? uint.MinValue : (uint)value;
|
value <= uint.MinValue ? uint.MinValue : (uint)value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static ulong SatF32ToU64(float value)
|
public static ulong SatF32ToU64(float value)
|
||||||
{
|
{
|
||||||
if (float.IsNaN(value))
|
if (float.IsNaN(value))
|
||||||
@@ -143,7 +171,11 @@ namespace ARMeilleure.Instructions
|
|||||||
value <= ulong.MinValue ? ulong.MinValue : (ulong)value;
|
value <= ulong.MinValue ? ulong.MinValue : (ulong)value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static int SatF64ToS32(double value)
|
public static int SatF64ToS32(double value)
|
||||||
{
|
{
|
||||||
if (double.IsNaN(value))
|
if (double.IsNaN(value))
|
||||||
@@ -155,7 +187,11 @@ namespace ARMeilleure.Instructions
|
|||||||
value <= int.MinValue ? int.MinValue : (int)value;
|
value <= int.MinValue ? int.MinValue : (int)value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static long SatF64ToS64(double value)
|
public static long SatF64ToS64(double value)
|
||||||
{
|
{
|
||||||
if (double.IsNaN(value))
|
if (double.IsNaN(value))
|
||||||
@@ -167,7 +203,11 @@ namespace ARMeilleure.Instructions
|
|||||||
value <= long.MinValue ? long.MinValue : (long)value;
|
value <= long.MinValue ? long.MinValue : (long)value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static uint SatF64ToU32(double value)
|
public static uint SatF64ToU32(double value)
|
||||||
{
|
{
|
||||||
if (double.IsNaN(value))
|
if (double.IsNaN(value))
|
||||||
@@ -179,7 +219,11 @@ namespace ARMeilleure.Instructions
|
|||||||
value <= uint.MinValue ? uint.MinValue : (uint)value;
|
value <= uint.MinValue ? uint.MinValue : (uint)value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static ulong SatF64ToU64(double value)
|
public static ulong SatF64ToU64(double value)
|
||||||
{
|
{
|
||||||
if (double.IsNaN(value))
|
if (double.IsNaN(value))
|
||||||
@@ -193,7 +237,11 @@ namespace ARMeilleure.Instructions
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region "Count"
|
#region "Count"
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static ulong CountLeadingSigns(ulong value, int size) // size is 8, 16, 32 or 64 (SIMD&FP or Base Inst.).
|
public static ulong CountLeadingSigns(ulong value, int size) // size is 8, 16, 32 or 64 (SIMD&FP or Base Inst.).
|
||||||
{
|
{
|
||||||
value ^= value >> 1;
|
value ^= value >> 1;
|
||||||
@@ -213,7 +261,11 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
private static ReadOnlySpan<byte> ClzNibbleTbl => [4, 3, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0];
|
private static ReadOnlySpan<byte> ClzNibbleTbl => [4, 3, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static ulong CountLeadingZeros(ulong value, int size) // size is 8, 16, 32 or 64 (SIMD&FP or Base Inst.).
|
public static ulong CountLeadingZeros(ulong value, int size) // size is 8, 16, 32 or 64 (SIMD&FP or Base Inst.).
|
||||||
{
|
{
|
||||||
if (value == 0ul)
|
if (value == 0ul)
|
||||||
@@ -237,49 +289,81 @@ namespace ARMeilleure.Instructions
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region "Table"
|
#region "Table"
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static V128 Tbl1(V128 vector, int bytes, V128 tb0)
|
public static V128 Tbl1(V128 vector, int bytes, V128 tb0)
|
||||||
{
|
{
|
||||||
return TblOrTbx(default, vector, bytes, tb0);
|
return TblOrTbx(default, vector, bytes, tb0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static V128 Tbl2(V128 vector, int bytes, V128 tb0, V128 tb1)
|
public static V128 Tbl2(V128 vector, int bytes, V128 tb0, V128 tb1)
|
||||||
{
|
{
|
||||||
return TblOrTbx(default, vector, bytes, tb0, tb1);
|
return TblOrTbx(default, vector, bytes, tb0, tb1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static V128 Tbl3(V128 vector, int bytes, V128 tb0, V128 tb1, V128 tb2)
|
public static V128 Tbl3(V128 vector, int bytes, V128 tb0, V128 tb1, V128 tb2)
|
||||||
{
|
{
|
||||||
return TblOrTbx(default, vector, bytes, tb0, tb1, tb2);
|
return TblOrTbx(default, vector, bytes, tb0, tb1, tb2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static V128 Tbl4(V128 vector, int bytes, V128 tb0, V128 tb1, V128 tb2, V128 tb3)
|
public static V128 Tbl4(V128 vector, int bytes, V128 tb0, V128 tb1, V128 tb2, V128 tb3)
|
||||||
{
|
{
|
||||||
return TblOrTbx(default, vector, bytes, tb0, tb1, tb2, tb3);
|
return TblOrTbx(default, vector, bytes, tb0, tb1, tb2, tb3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static V128 Tbx1(V128 dest, V128 vector, int bytes, V128 tb0)
|
public static V128 Tbx1(V128 dest, V128 vector, int bytes, V128 tb0)
|
||||||
{
|
{
|
||||||
return TblOrTbx(dest, vector, bytes, tb0);
|
return TblOrTbx(dest, vector, bytes, tb0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static V128 Tbx2(V128 dest, V128 vector, int bytes, V128 tb0, V128 tb1)
|
public static V128 Tbx2(V128 dest, V128 vector, int bytes, V128 tb0, V128 tb1)
|
||||||
{
|
{
|
||||||
return TblOrTbx(dest, vector, bytes, tb0, tb1);
|
return TblOrTbx(dest, vector, bytes, tb0, tb1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static V128 Tbx3(V128 dest, V128 vector, int bytes, V128 tb0, V128 tb1, V128 tb2)
|
public static V128 Tbx3(V128 dest, V128 vector, int bytes, V128 tb0, V128 tb1, V128 tb2)
|
||||||
{
|
{
|
||||||
return TblOrTbx(dest, vector, bytes, tb0, tb1, tb2);
|
return TblOrTbx(dest, vector, bytes, tb0, tb1, tb2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static V128 Tbx4(V128 dest, V128 vector, int bytes, V128 tb0, V128 tb1, V128 tb2, V128 tb3)
|
public static V128 Tbx4(V128 dest, V128 vector, int bytes, V128 tb0, V128 tb1, V128 tb2, V128 tb3)
|
||||||
{
|
{
|
||||||
return TblOrTbx(dest, vector, bytes, tb0, tb1, tb2, tb3);
|
return TblOrTbx(dest, vector, bytes, tb0, tb1, tb2, tb3);
|
||||||
@@ -321,22 +405,54 @@ namespace ARMeilleure.Instructions
|
|||||||
private const uint Crc32RevPoly = 0xedb88320;
|
private const uint Crc32RevPoly = 0xedb88320;
|
||||||
private const uint Crc32cRevPoly = 0x82f63b78;
|
private const uint Crc32cRevPoly = 0x82f63b78;
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static uint Crc32b(uint crc, byte value) => Crc32(crc, Crc32RevPoly, value);
|
public static uint Crc32b(uint crc, byte value) => Crc32(crc, Crc32RevPoly, value);
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static uint Crc32h(uint crc, ushort value) => Crc32h(crc, Crc32RevPoly, value);
|
public static uint Crc32h(uint crc, ushort value) => Crc32h(crc, Crc32RevPoly, value);
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static uint Crc32w(uint crc, uint value) => Crc32w(crc, Crc32RevPoly, value);
|
public static uint Crc32w(uint crc, uint value) => Crc32w(crc, Crc32RevPoly, value);
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static uint Crc32x(uint crc, ulong value) => Crc32x(crc, Crc32RevPoly, value);
|
public static uint Crc32x(uint crc, ulong value) => Crc32x(crc, Crc32RevPoly, value);
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static uint Crc32cb(uint crc, byte value) => Crc32(crc, Crc32cRevPoly, value);
|
public static uint Crc32cb(uint crc, byte value) => Crc32(crc, Crc32cRevPoly, value);
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static uint Crc32ch(uint crc, ushort value) => Crc32h(crc, Crc32cRevPoly, value);
|
public static uint Crc32ch(uint crc, ushort value) => Crc32h(crc, Crc32cRevPoly, value);
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static uint Crc32cw(uint crc, uint value) => Crc32w(crc, Crc32cRevPoly, value);
|
public static uint Crc32cw(uint crc, uint value) => Crc32w(crc, Crc32cRevPoly, value);
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static uint Crc32cx(uint crc, ulong value) => Crc32x(crc, Crc32cRevPoly, value);
|
public static uint Crc32cx(uint crc, ulong value) => Crc32x(crc, Crc32cRevPoly, value);
|
||||||
|
|
||||||
private static uint Crc32h(uint crc, uint poly, ushort val)
|
private static uint Crc32h(uint crc, uint poly, ushort val)
|
||||||
@@ -387,25 +503,41 @@ namespace ARMeilleure.Instructions
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region "Aes"
|
#region "Aes"
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static V128 Decrypt(V128 value, V128 roundKey)
|
public static V128 Decrypt(V128 value, V128 roundKey)
|
||||||
{
|
{
|
||||||
return CryptoHelper.AesInvSubBytes(CryptoHelper.AesInvShiftRows(value ^ roundKey));
|
return CryptoHelper.AesInvSubBytes(CryptoHelper.AesInvShiftRows(value ^ roundKey));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static V128 Encrypt(V128 value, V128 roundKey)
|
public static V128 Encrypt(V128 value, V128 roundKey)
|
||||||
{
|
{
|
||||||
return CryptoHelper.AesSubBytes(CryptoHelper.AesShiftRows(value ^ roundKey));
|
return CryptoHelper.AesSubBytes(CryptoHelper.AesShiftRows(value ^ roundKey));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static V128 InverseMixColumns(V128 value)
|
public static V128 InverseMixColumns(V128 value)
|
||||||
{
|
{
|
||||||
return CryptoHelper.AesInvMixColumns(value);
|
return CryptoHelper.AesInvMixColumns(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static V128 MixColumns(V128 value)
|
public static V128 MixColumns(V128 value)
|
||||||
{
|
{
|
||||||
return CryptoHelper.AesMixColumns(value);
|
return CryptoHelper.AesMixColumns(value);
|
||||||
@@ -413,7 +545,11 @@ namespace ARMeilleure.Instructions
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region "Sha1"
|
#region "Sha1"
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static V128 HashChoose(V128 hash_abcd, uint hash_e, V128 wk)
|
public static V128 HashChoose(V128 hash_abcd, uint hash_e, V128 wk)
|
||||||
{
|
{
|
||||||
for (int e = 0; e <= 3; e++)
|
for (int e = 0; e <= 3; e++)
|
||||||
@@ -434,13 +570,21 @@ namespace ARMeilleure.Instructions
|
|||||||
return hash_abcd;
|
return hash_abcd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static uint FixedRotate(uint hash_e)
|
public static uint FixedRotate(uint hash_e)
|
||||||
{
|
{
|
||||||
return hash_e.Rol(30);
|
return hash_e.Rol(30);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static V128 HashMajority(V128 hash_abcd, uint hash_e, V128 wk)
|
public static V128 HashMajority(V128 hash_abcd, uint hash_e, V128 wk)
|
||||||
{
|
{
|
||||||
for (int e = 0; e <= 3; e++)
|
for (int e = 0; e <= 3; e++)
|
||||||
@@ -461,7 +605,11 @@ namespace ARMeilleure.Instructions
|
|||||||
return hash_abcd;
|
return hash_abcd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static V128 HashParity(V128 hash_abcd, uint hash_e, V128 wk)
|
public static V128 HashParity(V128 hash_abcd, uint hash_e, V128 wk)
|
||||||
{
|
{
|
||||||
for (int e = 0; e <= 3; e++)
|
for (int e = 0; e <= 3; e++)
|
||||||
@@ -482,7 +630,11 @@ namespace ARMeilleure.Instructions
|
|||||||
return hash_abcd;
|
return hash_abcd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static V128 Sha1SchedulePart1(V128 w0_3, V128 w4_7, V128 w8_11)
|
public static V128 Sha1SchedulePart1(V128 w0_3, V128 w4_7, V128 w8_11)
|
||||||
{
|
{
|
||||||
ulong t2 = w4_7.Extract<ulong>(0);
|
ulong t2 = w4_7.Extract<ulong>(0);
|
||||||
@@ -493,7 +645,11 @@ namespace ARMeilleure.Instructions
|
|||||||
return result ^ (w0_3 ^ w8_11);
|
return result ^ (w0_3 ^ w8_11);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static V128 Sha1SchedulePart2(V128 tw0_3, V128 w12_15)
|
public static V128 Sha1SchedulePart2(V128 tw0_3, V128 w12_15)
|
||||||
{
|
{
|
||||||
V128 t = tw0_3 ^ (w12_15 >> 32);
|
V128 t = tw0_3 ^ (w12_15 >> 32);
|
||||||
@@ -538,19 +694,31 @@ namespace ARMeilleure.Instructions
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region "Sha256"
|
#region "Sha256"
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static V128 HashLower(V128 hash_abcd, V128 hash_efgh, V128 wk)
|
public static V128 HashLower(V128 hash_abcd, V128 hash_efgh, V128 wk)
|
||||||
{
|
{
|
||||||
return Sha256Hash(hash_abcd, hash_efgh, wk, part1: true);
|
return Sha256Hash(hash_abcd, hash_efgh, wk, part1: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static V128 HashUpper(V128 hash_abcd, V128 hash_efgh, V128 wk)
|
public static V128 HashUpper(V128 hash_abcd, V128 hash_efgh, V128 wk)
|
||||||
{
|
{
|
||||||
return Sha256Hash(hash_abcd, hash_efgh, wk, part1: false);
|
return Sha256Hash(hash_abcd, hash_efgh, wk, part1: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static V128 Sha256SchedulePart1(V128 w0_3, V128 w4_7)
|
public static V128 Sha256SchedulePart1(V128 w0_3, V128 w4_7)
|
||||||
{
|
{
|
||||||
V128 result = new();
|
V128 result = new();
|
||||||
@@ -569,7 +737,11 @@ namespace ARMeilleure.Instructions
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static V128 Sha256SchedulePart2(V128 w0_3, V128 w8_11, V128 w12_15)
|
public static V128 Sha256SchedulePart2(V128 w0_3, V128 w8_11, V128 w12_15)
|
||||||
{
|
{
|
||||||
V128 result = new();
|
V128 result = new();
|
||||||
@@ -671,7 +843,11 @@ namespace ARMeilleure.Instructions
|
|||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static V128 PolynomialMult64_128(ulong op1, ulong op2)
|
public static V128 PolynomialMult64_128(ulong op1, ulong op2)
|
||||||
{
|
{
|
||||||
V128 result = V128.Zero;
|
V128 result = V128.Zero;
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
using ARMeilleure.State;
|
using ARMeilleure.State;
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
#if ANDROID
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
#else
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace ARMeilleure.Instructions
|
namespace ARMeilleure.Instructions
|
||||||
{
|
{
|
||||||
@@ -313,7 +317,11 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
static class SoftFloat16_32
|
static class SoftFloat16_32
|
||||||
{
|
{
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static float FPConvert(ushort valueBits)
|
public static float FPConvert(ushort valueBits)
|
||||||
{
|
{
|
||||||
ExecutionContext context = NativeInterface.GetContext();
|
ExecutionContext context = NativeInterface.GetContext();
|
||||||
@@ -489,7 +497,11 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
static class SoftFloat16_64
|
static class SoftFloat16_64
|
||||||
{
|
{
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static double FPConvert(ushort valueBits)
|
public static double FPConvert(ushort valueBits)
|
||||||
{
|
{
|
||||||
ExecutionContext context = NativeInterface.GetContext();
|
ExecutionContext context = NativeInterface.GetContext();
|
||||||
@@ -665,7 +677,11 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
static class SoftFloat32_16
|
static class SoftFloat32_16
|
||||||
{
|
{
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static ushort FPConvert(float value)
|
public static ushort FPConvert(float value)
|
||||||
{
|
{
|
||||||
ExecutionContext context = NativeInterface.GetContext();
|
ExecutionContext context = NativeInterface.GetContext();
|
||||||
@@ -785,13 +801,21 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
static class SoftFloat32
|
static class SoftFloat32
|
||||||
{
|
{
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static float FPAdd(float value1, float value2)
|
public static float FPAdd(float value1, float value2)
|
||||||
{
|
{
|
||||||
return FPAddFpscrImpl(value1, value2, false);
|
return FPAddFpscrImpl(value1, value2, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static float FPAddFpscr(float value1, float value2, byte standardFpscr)
|
public static float FPAddFpscr(float value1, float value2, byte standardFpscr)
|
||||||
{
|
{
|
||||||
return FPAddFpscrImpl(value1, value2, standardFpscr == 1);
|
return FPAddFpscrImpl(value1, value2, standardFpscr == 1);
|
||||||
@@ -848,7 +872,11 @@ namespace ARMeilleure.Instructions
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static int FPCompare(float value1, float value2, byte signalNaNs)
|
public static int FPCompare(float value1, float value2, byte signalNaNs)
|
||||||
{
|
{
|
||||||
ExecutionContext context = NativeInterface.GetContext();
|
ExecutionContext context = NativeInterface.GetContext();
|
||||||
@@ -887,7 +915,11 @@ namespace ARMeilleure.Instructions
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static float FPCompareEQ(float value1, float value2)
|
public static float FPCompareEQ(float value1, float value2)
|
||||||
{
|
{
|
||||||
return FPCompareEQFpscrImpl(value1, value2, false);
|
return FPCompareEQFpscrImpl(value1, value2, false);
|
||||||
@@ -920,19 +952,31 @@ namespace ARMeilleure.Instructions
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static float FPCompareEQFpscr(float value1, float value2, byte standardFpscr)
|
public static float FPCompareEQFpscr(float value1, float value2, byte standardFpscr)
|
||||||
{
|
{
|
||||||
return FPCompareEQFpscrImpl(value1, value2, standardFpscr == 1);
|
return FPCompareEQFpscrImpl(value1, value2, standardFpscr == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static float FPCompareGE(float value1, float value2)
|
public static float FPCompareGE(float value1, float value2)
|
||||||
{
|
{
|
||||||
return FPCompareGEFpscrImpl(value1, value2, false);
|
return FPCompareGEFpscrImpl(value1, value2, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static float FPCompareGEFpscr(float value1, float value2, byte standardFpscr)
|
public static float FPCompareGEFpscr(float value1, float value2, byte standardFpscr)
|
||||||
{
|
{
|
||||||
return FPCompareGEFpscrImpl(value1, value2, standardFpscr == 1);
|
return FPCompareGEFpscrImpl(value1, value2, standardFpscr == 1);
|
||||||
@@ -962,13 +1006,21 @@ namespace ARMeilleure.Instructions
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static float FPCompareGT(float value1, float value2)
|
public static float FPCompareGT(float value1, float value2)
|
||||||
{
|
{
|
||||||
return FPCompareGTFpscrImpl(value1, value2, false);
|
return FPCompareGTFpscrImpl(value1, value2, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static float FPCompareGTFpscr(float value1, float value2, byte standardFpscr)
|
public static float FPCompareGTFpscr(float value1, float value2, byte standardFpscr)
|
||||||
{
|
{
|
||||||
return FPCompareGTFpscrImpl(value1, value2, standardFpscr == 1);
|
return FPCompareGTFpscrImpl(value1, value2, standardFpscr == 1);
|
||||||
@@ -998,31 +1050,51 @@ namespace ARMeilleure.Instructions
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static float FPCompareLE(float value1, float value2)
|
public static float FPCompareLE(float value1, float value2)
|
||||||
{
|
{
|
||||||
return FPCompareGEFpscrImpl(value2, value1, false);
|
return FPCompareGEFpscrImpl(value2, value1, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static float FPCompareLT(float value1, float value2)
|
public static float FPCompareLT(float value1, float value2)
|
||||||
{
|
{
|
||||||
return FPCompareGTFpscrImpl(value2, value1, false);
|
return FPCompareGTFpscrImpl(value2, value1, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static float FPCompareLEFpscr(float value1, float value2, byte standardFpscr)
|
public static float FPCompareLEFpscr(float value1, float value2, byte standardFpscr)
|
||||||
{
|
{
|
||||||
return FPCompareGEFpscrImpl(value2, value1, standardFpscr == 1);
|
return FPCompareGEFpscrImpl(value2, value1, standardFpscr == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static float FPCompareLTFpscr(float value1, float value2, byte standardFpscr)
|
public static float FPCompareLTFpscr(float value1, float value2, byte standardFpscr)
|
||||||
{
|
{
|
||||||
return FPCompareGEFpscrImpl(value2, value1, standardFpscr == 1);
|
return FPCompareGEFpscrImpl(value2, value1, standardFpscr == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static float FPDiv(float value1, float value2)
|
public static float FPDiv(float value1, float value2)
|
||||||
{
|
{
|
||||||
ExecutionContext context = NativeInterface.GetContext();
|
ExecutionContext context = NativeInterface.GetContext();
|
||||||
@@ -1075,13 +1147,21 @@ namespace ARMeilleure.Instructions
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static float FPMax(float value1, float value2)
|
public static float FPMax(float value1, float value2)
|
||||||
{
|
{
|
||||||
return FPMaxFpscrImpl(value1, value2, false);
|
return FPMaxFpscrImpl(value1, value2, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static float FPMaxFpscr(float value1, float value2, byte standardFpscr)
|
public static float FPMaxFpscr(float value1, float value2, byte standardFpscr)
|
||||||
{
|
{
|
||||||
return FPMaxFpscrImpl(value1, value2, standardFpscr == 1);
|
return FPMaxFpscrImpl(value1, value2, standardFpscr == 1);
|
||||||
@@ -1148,7 +1228,11 @@ namespace ARMeilleure.Instructions
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static float FPMaxNum(float value1, float value2)
|
public static float FPMaxNum(float value1, float value2)
|
||||||
{
|
{
|
||||||
return FPMaxNumFpscrImpl(value1, value2, false);
|
return FPMaxNumFpscrImpl(value1, value2, false);
|
||||||
@@ -1174,19 +1258,31 @@ namespace ARMeilleure.Instructions
|
|||||||
return FPMaxFpscrImpl(value1, value2, standardFpscr);
|
return FPMaxFpscrImpl(value1, value2, standardFpscr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static float FPMaxNumFpscr(float value1, float value2, byte standardFpscr)
|
public static float FPMaxNumFpscr(float value1, float value2, byte standardFpscr)
|
||||||
{
|
{
|
||||||
return FPMaxNumFpscrImpl(value1, value2, standardFpscr == 1);
|
return FPMaxNumFpscrImpl(value1, value2, standardFpscr == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static float FPMin(float value1, float value2)
|
public static float FPMin(float value1, float value2)
|
||||||
{
|
{
|
||||||
return FPMinFpscrImpl(value1, value2, false);
|
return FPMinFpscrImpl(value1, value2, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static float FPMinFpscr(float value1, float value2, byte standardFpscr)
|
public static float FPMinFpscr(float value1, float value2, byte standardFpscr)
|
||||||
{
|
{
|
||||||
return FPMinFpscrImpl(value1, value2, standardFpscr == 1);
|
return FPMinFpscrImpl(value1, value2, standardFpscr == 1);
|
||||||
@@ -1253,13 +1349,21 @@ namespace ARMeilleure.Instructions
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static float FPMinNum(float value1, float value2)
|
public static float FPMinNum(float value1, float value2)
|
||||||
{
|
{
|
||||||
return FPMinNumFpscrImpl(value1, value2, false);
|
return FPMinNumFpscrImpl(value1, value2, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static float FPMinNumFpscr(float value1, float value2, byte standardFpscr)
|
public static float FPMinNumFpscr(float value1, float value2, byte standardFpscr)
|
||||||
{
|
{
|
||||||
return FPMinNumFpscrImpl(value1, value2, standardFpscr == 1);
|
return FPMinNumFpscrImpl(value1, value2, standardFpscr == 1);
|
||||||
@@ -1285,13 +1389,21 @@ namespace ARMeilleure.Instructions
|
|||||||
return FPMinFpscrImpl(value1, value2, standardFpscr);
|
return FPMinFpscrImpl(value1, value2, standardFpscr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static float FPMul(float value1, float value2)
|
public static float FPMul(float value1, float value2)
|
||||||
{
|
{
|
||||||
return FPMulFpscrImpl(value1, value2, false);
|
return FPMulFpscrImpl(value1, value2, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static float FPMulFpscr(float value1, float value2, byte standardFpscr)
|
public static float FPMulFpscr(float value1, float value2, byte standardFpscr)
|
||||||
{
|
{
|
||||||
return FPMulFpscrImpl(value1, value2, standardFpscr == 1);
|
return FPMulFpscrImpl(value1, value2, standardFpscr == 1);
|
||||||
@@ -1344,13 +1456,21 @@ namespace ARMeilleure.Instructions
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static float FPMulAdd(float valueA, float value1, float value2)
|
public static float FPMulAdd(float valueA, float value1, float value2)
|
||||||
{
|
{
|
||||||
return FPMulAddFpscrImpl(valueA, value1, value2, false);
|
return FPMulAddFpscrImpl(valueA, value1, value2, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static float FPMulAddFpscr(float valueA, float value1, float value2, byte standardFpscr)
|
public static float FPMulAddFpscr(float valueA, float value1, float value2, byte standardFpscr)
|
||||||
{
|
{
|
||||||
return FPMulAddFpscrImpl(valueA, value1, value2, standardFpscr == 1);
|
return FPMulAddFpscrImpl(valueA, value1, value2, standardFpscr == 1);
|
||||||
@@ -1422,7 +1542,11 @@ namespace ARMeilleure.Instructions
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static float FPMulSub(float valueA, float value1, float value2)
|
public static float FPMulSub(float valueA, float value1, float value2)
|
||||||
{
|
{
|
||||||
value1 = value1.FPNeg();
|
value1 = value1.FPNeg();
|
||||||
@@ -1430,7 +1554,11 @@ namespace ARMeilleure.Instructions
|
|||||||
return FPMulAddFpscrImpl(valueA, value1, value2, false);
|
return FPMulAddFpscrImpl(valueA, value1, value2, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static float FPMulSubFpscr(float valueA, float value1, float value2, byte standardFpscr)
|
public static float FPMulSubFpscr(float valueA, float value1, float value2, byte standardFpscr)
|
||||||
{
|
{
|
||||||
value1 = value1.FPNeg();
|
value1 = value1.FPNeg();
|
||||||
@@ -1438,7 +1566,11 @@ namespace ARMeilleure.Instructions
|
|||||||
return FPMulAddFpscrImpl(valueA, value1, value2, standardFpscr == 1);
|
return FPMulAddFpscrImpl(valueA, value1, value2, standardFpscr == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static float FPMulX(float value1, float value2)
|
public static float FPMulX(float value1, float value2)
|
||||||
{
|
{
|
||||||
ExecutionContext context = NativeInterface.GetContext();
|
ExecutionContext context = NativeInterface.GetContext();
|
||||||
@@ -1484,7 +1616,11 @@ namespace ARMeilleure.Instructions
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static float FPNegMulAdd(float valueA, float value1, float value2)
|
public static float FPNegMulAdd(float valueA, float value1, float value2)
|
||||||
{
|
{
|
||||||
valueA = valueA.FPNeg();
|
valueA = valueA.FPNeg();
|
||||||
@@ -1493,7 +1629,11 @@ namespace ARMeilleure.Instructions
|
|||||||
return FPMulAddFpscrImpl(valueA, value1, value2, false);
|
return FPMulAddFpscrImpl(valueA, value1, value2, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static float FPNegMulSub(float valueA, float value1, float value2)
|
public static float FPNegMulSub(float valueA, float value1, float value2)
|
||||||
{
|
{
|
||||||
valueA = valueA.FPNeg();
|
valueA = valueA.FPNeg();
|
||||||
@@ -1501,13 +1641,21 @@ namespace ARMeilleure.Instructions
|
|||||||
return FPMulAddFpscrImpl(valueA, value1, value2, false);
|
return FPMulAddFpscrImpl(valueA, value1, value2, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static float FPRecipEstimate(float value)
|
public static float FPRecipEstimate(float value)
|
||||||
{
|
{
|
||||||
return FPRecipEstimateFpscrImpl(value, false);
|
return FPRecipEstimateFpscrImpl(value, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static float FPRecipEstimateFpscr(float value, byte standardFpscr)
|
public static float FPRecipEstimateFpscr(float value, byte standardFpscr)
|
||||||
{
|
{
|
||||||
return FPRecipEstimateFpscrImpl(value, standardFpscr == 1);
|
return FPRecipEstimateFpscrImpl(value, standardFpscr == 1);
|
||||||
@@ -1600,7 +1748,11 @@ namespace ARMeilleure.Instructions
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static float FPRecipStep(float value1, float value2)
|
public static float FPRecipStep(float value1, float value2)
|
||||||
{
|
{
|
||||||
ExecutionContext context = NativeInterface.GetContext();
|
ExecutionContext context = NativeInterface.GetContext();
|
||||||
@@ -1635,7 +1787,11 @@ namespace ARMeilleure.Instructions
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static float FPRecipStepFused(float value1, float value2)
|
public static float FPRecipStepFused(float value1, float value2)
|
||||||
{
|
{
|
||||||
ExecutionContext context = NativeInterface.GetContext();
|
ExecutionContext context = NativeInterface.GetContext();
|
||||||
@@ -1679,7 +1835,11 @@ namespace ARMeilleure.Instructions
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static float FPRecpX(float value)
|
public static float FPRecpX(float value)
|
||||||
{
|
{
|
||||||
ExecutionContext context = NativeInterface.GetContext();
|
ExecutionContext context = NativeInterface.GetContext();
|
||||||
@@ -1705,13 +1865,21 @@ namespace ARMeilleure.Instructions
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static float FPRSqrtEstimate(float value)
|
public static float FPRSqrtEstimate(float value)
|
||||||
{
|
{
|
||||||
return FPRSqrtEstimateFpscrImpl(value, false);
|
return FPRSqrtEstimateFpscrImpl(value, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static float FPRSqrtEstimateFpscr(float value, byte standardFpscr)
|
public static float FPRSqrtEstimateFpscr(float value, byte standardFpscr)
|
||||||
{
|
{
|
||||||
return FPRSqrtEstimateFpscrImpl(value, standardFpscr == 1);
|
return FPRSqrtEstimateFpscrImpl(value, standardFpscr == 1);
|
||||||
@@ -1831,7 +1999,11 @@ namespace ARMeilleure.Instructions
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static float FPRSqrtStep(float value1, float value2)
|
public static float FPRSqrtStep(float value1, float value2)
|
||||||
{
|
{
|
||||||
ExecutionContext context = NativeInterface.GetContext();
|
ExecutionContext context = NativeInterface.GetContext();
|
||||||
@@ -1866,7 +2038,11 @@ namespace ARMeilleure.Instructions
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static float FPRSqrtStepFused(float value1, float value2)
|
public static float FPRSqrtStepFused(float value1, float value2)
|
||||||
{
|
{
|
||||||
ExecutionContext context = NativeInterface.GetContext();
|
ExecutionContext context = NativeInterface.GetContext();
|
||||||
@@ -1910,7 +2086,11 @@ namespace ARMeilleure.Instructions
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static float FPSqrt(float value)
|
public static float FPSqrt(float value)
|
||||||
{
|
{
|
||||||
ExecutionContext context = NativeInterface.GetContext();
|
ExecutionContext context = NativeInterface.GetContext();
|
||||||
@@ -1953,7 +2133,11 @@ namespace ARMeilleure.Instructions
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static float FPSub(float value1, float value2)
|
public static float FPSub(float value1, float value2)
|
||||||
{
|
{
|
||||||
return FPSubFpscrImpl(value1, value2, false);
|
return FPSubFpscrImpl(value1, value2, false);
|
||||||
@@ -2200,7 +2384,11 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
static class SoftFloat64_16
|
static class SoftFloat64_16
|
||||||
{
|
{
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static ushort FPConvert(double value)
|
public static ushort FPConvert(double value)
|
||||||
{
|
{
|
||||||
ExecutionContext context = NativeInterface.GetContext();
|
ExecutionContext context = NativeInterface.GetContext();
|
||||||
@@ -2320,13 +2508,21 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
static class SoftFloat64
|
static class SoftFloat64
|
||||||
{
|
{
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static double FPAdd(double value1, double value2)
|
public static double FPAdd(double value1, double value2)
|
||||||
{
|
{
|
||||||
return FPAddFpscrImpl(value1, value2, false);
|
return FPAddFpscrImpl(value1, value2, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static double FPAddFpscr(double value1, double value2, byte standardFpscr)
|
public static double FPAddFpscr(double value1, double value2, byte standardFpscr)
|
||||||
{
|
{
|
||||||
return FPAddFpscrImpl(value1, value2, standardFpscr == 1);
|
return FPAddFpscrImpl(value1, value2, standardFpscr == 1);
|
||||||
@@ -2383,7 +2579,11 @@ namespace ARMeilleure.Instructions
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static int FPCompare(double value1, double value2, byte signalNaNs)
|
public static int FPCompare(double value1, double value2, byte signalNaNs)
|
||||||
{
|
{
|
||||||
ExecutionContext context = NativeInterface.GetContext();
|
ExecutionContext context = NativeInterface.GetContext();
|
||||||
@@ -2422,13 +2622,21 @@ namespace ARMeilleure.Instructions
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static double FPCompareEQ(double value1, double value2)
|
public static double FPCompareEQ(double value1, double value2)
|
||||||
{
|
{
|
||||||
return FPCompareEQFpscrImpl(value1, value2, false);
|
return FPCompareEQFpscrImpl(value1, value2, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static double FPCompareEQFpscr(double value1, double value2, byte standardFpscr)
|
public static double FPCompareEQFpscr(double value1, double value2, byte standardFpscr)
|
||||||
{
|
{
|
||||||
return FPCompareEQFpscrImpl(value1, value2, standardFpscr == 1);
|
return FPCompareEQFpscrImpl(value1, value2, standardFpscr == 1);
|
||||||
@@ -2461,13 +2669,21 @@ namespace ARMeilleure.Instructions
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static double FPCompareGE(double value1, double value2)
|
public static double FPCompareGE(double value1, double value2)
|
||||||
{
|
{
|
||||||
return FPCompareGEFpscrImpl(value1, value2, false);
|
return FPCompareGEFpscrImpl(value1, value2, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static double FPCompareGEFpscr(double value1, double value2, byte standardFpscr)
|
public static double FPCompareGEFpscr(double value1, double value2, byte standardFpscr)
|
||||||
{
|
{
|
||||||
return FPCompareGEFpscrImpl(value1, value2, standardFpscr == 1);
|
return FPCompareGEFpscrImpl(value1, value2, standardFpscr == 1);
|
||||||
@@ -2497,13 +2713,21 @@ namespace ARMeilleure.Instructions
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static double FPCompareGT(double value1, double value2)
|
public static double FPCompareGT(double value1, double value2)
|
||||||
{
|
{
|
||||||
return FPCompareGTFpscrImpl(value1, value2, false);
|
return FPCompareGTFpscrImpl(value1, value2, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static double FPCompareGTFpscr(double value1, double value2, byte standardFpscr)
|
public static double FPCompareGTFpscr(double value1, double value2, byte standardFpscr)
|
||||||
{
|
{
|
||||||
return FPCompareGTFpscrImpl(value1, value2, standardFpscr == 1);
|
return FPCompareGTFpscrImpl(value1, value2, standardFpscr == 1);
|
||||||
@@ -2533,31 +2757,51 @@ namespace ARMeilleure.Instructions
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static double FPCompareLE(double value1, double value2)
|
public static double FPCompareLE(double value1, double value2)
|
||||||
{
|
{
|
||||||
return FPCompareGEFpscrImpl(value2, value1, false);
|
return FPCompareGEFpscrImpl(value2, value1, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static double FPCompareLT(double value1, double value2)
|
public static double FPCompareLT(double value1, double value2)
|
||||||
{
|
{
|
||||||
return FPCompareGTFpscrImpl(value2, value1, false);
|
return FPCompareGTFpscrImpl(value2, value1, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static double FPCompareLEFpscr(double value1, double value2, byte standardFpscr)
|
public static double FPCompareLEFpscr(double value1, double value2, byte standardFpscr)
|
||||||
{
|
{
|
||||||
return FPCompareGEFpscrImpl(value2, value1, standardFpscr == 1);
|
return FPCompareGEFpscrImpl(value2, value1, standardFpscr == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static double FPCompareLTFpscr(double value1, double value2, byte standardFpscr)
|
public static double FPCompareLTFpscr(double value1, double value2, byte standardFpscr)
|
||||||
{
|
{
|
||||||
return FPCompareGTFpscrImpl(value2, value1, standardFpscr == 1);
|
return FPCompareGTFpscrImpl(value2, value1, standardFpscr == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static double FPDiv(double value1, double value2)
|
public static double FPDiv(double value1, double value2)
|
||||||
{
|
{
|
||||||
ExecutionContext context = NativeInterface.GetContext();
|
ExecutionContext context = NativeInterface.GetContext();
|
||||||
@@ -2610,13 +2854,21 @@ namespace ARMeilleure.Instructions
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static double FPMax(double value1, double value2)
|
public static double FPMax(double value1, double value2)
|
||||||
{
|
{
|
||||||
return FPMaxFpscrImpl(value1, value2, false);
|
return FPMaxFpscrImpl(value1, value2, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static double FPMaxFpscr(double value1, double value2, byte standardFpscr)
|
public static double FPMaxFpscr(double value1, double value2, byte standardFpscr)
|
||||||
{
|
{
|
||||||
return FPMaxFpscrImpl(value1, value2, standardFpscr == 1);
|
return FPMaxFpscrImpl(value1, value2, standardFpscr == 1);
|
||||||
@@ -2683,13 +2935,21 @@ namespace ARMeilleure.Instructions
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static double FPMaxNum(double value1, double value2)
|
public static double FPMaxNum(double value1, double value2)
|
||||||
{
|
{
|
||||||
return FPMaxNumFpscrImpl(value1, value2, false);
|
return FPMaxNumFpscrImpl(value1, value2, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static double FPMaxNumFpscr(double value1, double value2, byte standardFpscr)
|
public static double FPMaxNumFpscr(double value1, double value2, byte standardFpscr)
|
||||||
{
|
{
|
||||||
return FPMaxNumFpscrImpl(value1, value2, standardFpscr == 1);
|
return FPMaxNumFpscrImpl(value1, value2, standardFpscr == 1);
|
||||||
@@ -2715,13 +2975,21 @@ namespace ARMeilleure.Instructions
|
|||||||
return FPMaxFpscrImpl(value1, value2, standardFpscr);
|
return FPMaxFpscrImpl(value1, value2, standardFpscr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static double FPMin(double value1, double value2)
|
public static double FPMin(double value1, double value2)
|
||||||
{
|
{
|
||||||
return FPMinFpscrImpl(value1, value2, false);
|
return FPMinFpscrImpl(value1, value2, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static double FPMinFpscr(double value1, double value2, byte standardFpscr)
|
public static double FPMinFpscr(double value1, double value2, byte standardFpscr)
|
||||||
{
|
{
|
||||||
return FPMinFpscrImpl(value1, value2, standardFpscr == 1);
|
return FPMinFpscrImpl(value1, value2, standardFpscr == 1);
|
||||||
@@ -2788,13 +3056,21 @@ namespace ARMeilleure.Instructions
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static double FPMinNum(double value1, double value2)
|
public static double FPMinNum(double value1, double value2)
|
||||||
{
|
{
|
||||||
return FPMinNumFpscrImpl(value1, value2, false);
|
return FPMinNumFpscrImpl(value1, value2, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static double FPMinNumFpscr(double value1, double value2, byte standardFpscr)
|
public static double FPMinNumFpscr(double value1, double value2, byte standardFpscr)
|
||||||
{
|
{
|
||||||
return FPMinNumFpscrImpl(value1, value2, standardFpscr == 1);
|
return FPMinNumFpscrImpl(value1, value2, standardFpscr == 1);
|
||||||
@@ -2820,13 +3096,21 @@ namespace ARMeilleure.Instructions
|
|||||||
return FPMinFpscrImpl(value1, value2, standardFpscr);
|
return FPMinFpscrImpl(value1, value2, standardFpscr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static double FPMul(double value1, double value2)
|
public static double FPMul(double value1, double value2)
|
||||||
{
|
{
|
||||||
return FPMulFpscrImpl(value1, value2, false);
|
return FPMulFpscrImpl(value1, value2, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static double FPMulFpscr(double value1, double value2, byte standardFpscr)
|
public static double FPMulFpscr(double value1, double value2, byte standardFpscr)
|
||||||
{
|
{
|
||||||
return FPMulFpscrImpl(value1, value2, standardFpscr == 1);
|
return FPMulFpscrImpl(value1, value2, standardFpscr == 1);
|
||||||
@@ -2879,13 +3163,21 @@ namespace ARMeilleure.Instructions
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static double FPMulAdd(double valueA, double value1, double value2)
|
public static double FPMulAdd(double valueA, double value1, double value2)
|
||||||
{
|
{
|
||||||
return FPMulAddFpscrImpl(valueA, value1, value2, false);
|
return FPMulAddFpscrImpl(valueA, value1, value2, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static double FPMulAddFpscr(double valueA, double value1, double value2, byte standardFpscr)
|
public static double FPMulAddFpscr(double valueA, double value1, double value2, byte standardFpscr)
|
||||||
{
|
{
|
||||||
return FPMulAddFpscrImpl(valueA, value1, value2, standardFpscr == 1);
|
return FPMulAddFpscrImpl(valueA, value1, value2, standardFpscr == 1);
|
||||||
@@ -2957,7 +3249,11 @@ namespace ARMeilleure.Instructions
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static double FPMulSub(double valueA, double value1, double value2)
|
public static double FPMulSub(double valueA, double value1, double value2)
|
||||||
{
|
{
|
||||||
value1 = value1.FPNeg();
|
value1 = value1.FPNeg();
|
||||||
@@ -2965,7 +3261,11 @@ namespace ARMeilleure.Instructions
|
|||||||
return FPMulAddFpscrImpl(valueA, value1, value2, false);
|
return FPMulAddFpscrImpl(valueA, value1, value2, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static double FPMulSubFpscr(double valueA, double value1, double value2, byte standardFpscr)
|
public static double FPMulSubFpscr(double valueA, double value1, double value2, byte standardFpscr)
|
||||||
{
|
{
|
||||||
value1 = value1.FPNeg();
|
value1 = value1.FPNeg();
|
||||||
@@ -2973,7 +3273,11 @@ namespace ARMeilleure.Instructions
|
|||||||
return FPMulAddFpscrImpl(valueA, value1, value2, standardFpscr == 1);
|
return FPMulAddFpscrImpl(valueA, value1, value2, standardFpscr == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static double FPMulX(double value1, double value2)
|
public static double FPMulX(double value1, double value2)
|
||||||
{
|
{
|
||||||
ExecutionContext context = NativeInterface.GetContext();
|
ExecutionContext context = NativeInterface.GetContext();
|
||||||
@@ -3019,7 +3323,11 @@ namespace ARMeilleure.Instructions
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static double FPNegMulAdd(double valueA, double value1, double value2)
|
public static double FPNegMulAdd(double valueA, double value1, double value2)
|
||||||
{
|
{
|
||||||
valueA = valueA.FPNeg();
|
valueA = valueA.FPNeg();
|
||||||
@@ -3028,7 +3336,11 @@ namespace ARMeilleure.Instructions
|
|||||||
return FPMulAddFpscrImpl(valueA, value1, value2, false);
|
return FPMulAddFpscrImpl(valueA, value1, value2, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static double FPNegMulSub(double valueA, double value1, double value2)
|
public static double FPNegMulSub(double valueA, double value1, double value2)
|
||||||
{
|
{
|
||||||
valueA = valueA.FPNeg();
|
valueA = valueA.FPNeg();
|
||||||
@@ -3036,13 +3348,21 @@ namespace ARMeilleure.Instructions
|
|||||||
return FPMulAddFpscrImpl(valueA, value1, value2, false);
|
return FPMulAddFpscrImpl(valueA, value1, value2, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static double FPRecipEstimate(double value)
|
public static double FPRecipEstimate(double value)
|
||||||
{
|
{
|
||||||
return FPRecipEstimateFpscrImpl(value, false);
|
return FPRecipEstimateFpscrImpl(value, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static double FPRecipEstimateFpscr(double value, byte standardFpscr)
|
public static double FPRecipEstimateFpscr(double value, byte standardFpscr)
|
||||||
{
|
{
|
||||||
return FPRecipEstimateFpscrImpl(value, standardFpscr == 1);
|
return FPRecipEstimateFpscrImpl(value, standardFpscr == 1);
|
||||||
@@ -3135,7 +3455,11 @@ namespace ARMeilleure.Instructions
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static double FPRecipStep(double value1, double value2)
|
public static double FPRecipStep(double value1, double value2)
|
||||||
{
|
{
|
||||||
ExecutionContext context = NativeInterface.GetContext();
|
ExecutionContext context = NativeInterface.GetContext();
|
||||||
@@ -3170,7 +3494,11 @@ namespace ARMeilleure.Instructions
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static double FPRecipStepFused(double value1, double value2)
|
public static double FPRecipStepFused(double value1, double value2)
|
||||||
{
|
{
|
||||||
ExecutionContext context = NativeInterface.GetContext();
|
ExecutionContext context = NativeInterface.GetContext();
|
||||||
@@ -3214,7 +3542,11 @@ namespace ARMeilleure.Instructions
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static double FPRecpX(double value)
|
public static double FPRecpX(double value)
|
||||||
{
|
{
|
||||||
ExecutionContext context = NativeInterface.GetContext();
|
ExecutionContext context = NativeInterface.GetContext();
|
||||||
@@ -3240,13 +3572,21 @@ namespace ARMeilleure.Instructions
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static double FPRSqrtEstimate(double value)
|
public static double FPRSqrtEstimate(double value)
|
||||||
{
|
{
|
||||||
return FPRSqrtEstimateFpscrImpl(value, false);
|
return FPRSqrtEstimateFpscrImpl(value, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static double FPRSqrtEstimateFpscr(double value, byte standardFpscr)
|
public static double FPRSqrtEstimateFpscr(double value, byte standardFpscr)
|
||||||
{
|
{
|
||||||
return FPRSqrtEstimateFpscrImpl(value, standardFpscr == 1);
|
return FPRSqrtEstimateFpscrImpl(value, standardFpscr == 1);
|
||||||
@@ -3366,7 +3706,11 @@ namespace ARMeilleure.Instructions
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static double FPRSqrtStep(double value1, double value2)
|
public static double FPRSqrtStep(double value1, double value2)
|
||||||
{
|
{
|
||||||
ExecutionContext context = NativeInterface.GetContext();
|
ExecutionContext context = NativeInterface.GetContext();
|
||||||
@@ -3401,7 +3745,11 @@ namespace ARMeilleure.Instructions
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static double FPRSqrtStepFused(double value1, double value2)
|
public static double FPRSqrtStepFused(double value1, double value2)
|
||||||
{
|
{
|
||||||
ExecutionContext context = NativeInterface.GetContext();
|
ExecutionContext context = NativeInterface.GetContext();
|
||||||
@@ -3445,7 +3793,11 @@ namespace ARMeilleure.Instructions
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static double FPSqrt(double value)
|
public static double FPSqrt(double value)
|
||||||
{
|
{
|
||||||
ExecutionContext context = NativeInterface.GetContext();
|
ExecutionContext context = NativeInterface.GetContext();
|
||||||
@@ -3488,7 +3840,11 @@ namespace ARMeilleure.Instructions
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#else
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
|
#endif
|
||||||
public static double FPSub(double value1, double value2)
|
public static double FPSub(double value1, double value2)
|
||||||
{
|
{
|
||||||
return FPSubFpscr(value1, value2, false);
|
return FPSubFpscr(value1, value2, false);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ using System;
|
|||||||
|
|
||||||
namespace ARMeilleure.Memory
|
namespace ARMeilleure.Memory
|
||||||
{
|
{
|
||||||
public class ReservedRegion : IDisposable
|
public class ReservedRegion
|
||||||
{
|
{
|
||||||
public const int DefaultGranularity = 65536; // Mapping granularity in Windows.
|
public const int DefaultGranularity = 65536; // Mapping granularity in Windows.
|
||||||
|
|
||||||
|
|||||||
@@ -8,11 +8,13 @@ namespace ARMeilleure
|
|||||||
// low-core count PPTC
|
// low-core count PPTC
|
||||||
public static bool EcoFriendly { get; set; } = false;
|
public static bool EcoFriendly { get; set; } = false;
|
||||||
|
|
||||||
|
// Jit cache eviction
|
||||||
|
public static bool CacheEviction { get; set; } = false;
|
||||||
|
|
||||||
public static bool FastFP { get; set; } = true;
|
public static bool FastFP { get; set; } = true;
|
||||||
|
|
||||||
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 EnableDeepCallRecursionProtection { get; set; } = true;
|
||||||
|
|
||||||
public static bool UseAdvSimdIfAvailable { get; set; } = true;
|
public static bool UseAdvSimdIfAvailable { get; set; } = true;
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ namespace ARMeilleure.Signal
|
|||||||
{
|
{
|
||||||
public static class NativeSignalHandlerGenerator
|
public static class NativeSignalHandlerGenerator
|
||||||
{
|
{
|
||||||
public const int MaxTrackedRanges = 16;
|
public const int MaxTrackedRanges = 8;
|
||||||
|
|
||||||
private const int StructAddressOffset = 0;
|
private const int StructAddressOffset = 0;
|
||||||
private const int StructWriteOffset = 4;
|
private const int StructWriteOffset = 4;
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using ARMeilleure.IntermediateRepresentation;
|
using ARMeilleure.IntermediateRepresentation;
|
||||||
using ARMeilleure.Translation;
|
using ARMeilleure.Translation;
|
||||||
using ARMeilleure.Translation.Cache;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||||
|
|
||||||
@@ -18,7 +17,7 @@ namespace ARMeilleure.Signal
|
|||||||
public delegate int DebugThreadLocalMapGetOrReserve(int threadId, int initialState);
|
public delegate int DebugThreadLocalMapGetOrReserve(int threadId, int initialState);
|
||||||
public delegate void DebugNativeWriteLoop(nint nativeWriteLoopPtr, nint writePtr);
|
public delegate void DebugNativeWriteLoop(nint nativeWriteLoopPtr, nint writePtr);
|
||||||
|
|
||||||
public static DebugPartialUnmap GenerateDebugPartialUnmap(JitCache jitCache)
|
public static DebugPartialUnmap GenerateDebugPartialUnmap()
|
||||||
{
|
{
|
||||||
EmitterContext context = new();
|
EmitterContext context = new();
|
||||||
|
|
||||||
@@ -32,10 +31,10 @@ namespace ARMeilleure.Signal
|
|||||||
|
|
||||||
OperandType[] argTypes = [OperandType.I64];
|
OperandType[] argTypes = [OperandType.I64];
|
||||||
|
|
||||||
return Compiler.Compile(cfg, argTypes, OperandType.I32, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<DebugPartialUnmap>(jitCache);
|
return Compiler.Compile(cfg, argTypes, OperandType.I32, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<DebugPartialUnmap>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static DebugThreadLocalMapGetOrReserve GenerateDebugThreadLocalMapGetOrReserve(JitCache jitCache, nint structPtr)
|
public static DebugThreadLocalMapGetOrReserve GenerateDebugThreadLocalMapGetOrReserve(nint structPtr)
|
||||||
{
|
{
|
||||||
EmitterContext context = new();
|
EmitterContext context = new();
|
||||||
|
|
||||||
@@ -49,10 +48,10 @@ namespace ARMeilleure.Signal
|
|||||||
|
|
||||||
OperandType[] argTypes = [OperandType.I64];
|
OperandType[] argTypes = [OperandType.I64];
|
||||||
|
|
||||||
return Compiler.Compile(cfg, argTypes, OperandType.I32, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<DebugThreadLocalMapGetOrReserve>(jitCache);
|
return Compiler.Compile(cfg, argTypes, OperandType.I32, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<DebugThreadLocalMapGetOrReserve>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static DebugNativeWriteLoop GenerateDebugNativeWriteLoop(JitCache jitCache)
|
public static DebugNativeWriteLoop GenerateDebugNativeWriteLoop()
|
||||||
{
|
{
|
||||||
EmitterContext context = new();
|
EmitterContext context = new();
|
||||||
|
|
||||||
@@ -78,7 +77,7 @@ namespace ARMeilleure.Signal
|
|||||||
|
|
||||||
OperandType[] argTypes = [OperandType.I64];
|
OperandType[] argTypes = [OperandType.I64];
|
||||||
|
|
||||||
return Compiler.Compile(cfg, argTypes, OperandType.None, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<DebugNativeWriteLoop>(jitCache);
|
return Compiler.Compile(cfg, argTypes, OperandType.None, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<DebugNativeWriteLoop>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using ARMeilleure.Memory;
|
using ARMeilleure.Memory;
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace ARMeilleure.State
|
namespace ARMeilleure.State
|
||||||
{
|
{
|
||||||
@@ -11,7 +10,7 @@ namespace ARMeilleure.State
|
|||||||
|
|
||||||
internal nint NativeContextPtr => _nativeContext.BasePtr;
|
internal nint NativeContextPtr => _nativeContext.BasePtr;
|
||||||
|
|
||||||
internal bool Interrupted { get; private set; }
|
private bool _interrupted;
|
||||||
|
|
||||||
private readonly ICounter _counter;
|
private readonly ICounter _counter;
|
||||||
|
|
||||||
@@ -68,8 +67,6 @@ 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
|
||||||
@@ -95,19 +92,14 @@ 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)
|
||||||
{
|
{
|
||||||
@@ -115,7 +107,6 @@ 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;
|
||||||
|
|
||||||
@@ -143,9 +134,9 @@ namespace ARMeilleure.State
|
|||||||
|
|
||||||
internal void CheckInterrupt()
|
internal void CheckInterrupt()
|
||||||
{
|
{
|
||||||
if (Interrupted)
|
if (_interrupted)
|
||||||
{
|
{
|
||||||
Interrupted = false;
|
_interrupted = false;
|
||||||
|
|
||||||
_interruptCallback?.Invoke(this);
|
_interruptCallback?.Invoke(this);
|
||||||
}
|
}
|
||||||
@@ -155,37 +146,16 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,12 +23,6 @@ namespace ARMeilleure.State
|
|||||||
public int Running;
|
public int Running;
|
||||||
public int CallDepth;
|
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();
|
||||||
@@ -46,11 +40,6 @@ 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;
|
||||||
}
|
}
|
||||||
@@ -280,11 +269,6 @@ 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()
|
public static int GetCallDepthOffset()
|
||||||
{
|
{
|
||||||
return StorageOffset(ref _dummyStorage, ref _dummyStorage.CallDepth);
|
return StorageOffset(ref _dummyStorage, ref _dummyStorage.CallDepth);
|
||||||
|
|||||||
@@ -52,7 +52,6 @@ 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,8 +66,7 @@ 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;
|
||||||
@@ -78,7 +76,6 @@ 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>();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ using System.Diagnostics.CodeAnalysis;
|
|||||||
|
|
||||||
namespace ARMeilleure.Translation.Cache
|
namespace ARMeilleure.Translation.Cache
|
||||||
{
|
{
|
||||||
public readonly struct CacheEntry : IComparable<CacheEntry>
|
readonly struct CacheEntry : IComparable<CacheEntry>
|
||||||
{
|
{
|
||||||
public int Offset { get; }
|
public int Offset { get; }
|
||||||
public int Size { get; }
|
public int Size { get; }
|
||||||
|
|||||||
@@ -23,30 +23,11 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly int _regionSize;
|
|
||||||
private int _regionCount;
|
|
||||||
|
|
||||||
private readonly List<MemoryBlock> _blocks = [];
|
private readonly List<MemoryBlock> _blocks = [];
|
||||||
|
|
||||||
public CacheMemoryAllocator(int regionSize, int initialRegionCount = 1)
|
public CacheMemoryAllocator(int capacity)
|
||||||
{
|
{
|
||||||
_regionCount = 0;
|
_blocks.Add(new MemoryBlock(0, capacity));
|
||||||
_regionSize = regionSize;
|
|
||||||
|
|
||||||
for (; initialRegionCount > 0; initialRegionCount--)
|
|
||||||
{
|
|
||||||
_blocks.Add(new MemoryBlock(_regionSize * _regionCount, _regionSize));
|
|
||||||
_regionCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddNewBlocks(int count)
|
|
||||||
{
|
|
||||||
for (; count > 0; count--)
|
|
||||||
{
|
|
||||||
_blocks.Add(new MemoryBlock(_regionSize * _regionCount, _regionSize));
|
|
||||||
_regionCount++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Allocate(int size)
|
public int Allocate(int size)
|
||||||
@@ -85,13 +66,12 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
index = ~index;
|
index = ~index;
|
||||||
}
|
}
|
||||||
|
|
||||||
int endOffs = block.Offset + block.Size;
|
if (index < _blocks.Count)
|
||||||
|
|
||||||
// Don't merge blocks from different allocations
|
|
||||||
if (index < _blocks.Count && endOffs % _regionSize != 0)
|
|
||||||
{
|
{
|
||||||
MemoryBlock next = _blocks[index];
|
MemoryBlock next = _blocks[index];
|
||||||
|
|
||||||
|
int endOffs = block.Offset + block.Size;
|
||||||
|
|
||||||
if (next.Offset == endOffs)
|
if (next.Offset == endOffs)
|
||||||
{
|
{
|
||||||
block = new MemoryBlock(block.Offset, block.Size + next.Size);
|
block = new MemoryBlock(block.Offset, block.Size + next.Size);
|
||||||
@@ -99,8 +79,7 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't merge blocks from different allocations
|
if (index > 0)
|
||||||
if (index > 0 && block.Offset % _regionSize != 0)
|
|
||||||
{
|
{
|
||||||
MemoryBlock prev = _blocks[index - 1];
|
MemoryBlock prev = _blocks[index - 1];
|
||||||
|
|
||||||
|
|||||||
@@ -2,70 +2,140 @@ using ARMeilleure.CodeGen;
|
|||||||
using ARMeilleure.CodeGen.Unwinding;
|
using ARMeilleure.CodeGen.Unwinding;
|
||||||
using ARMeilleure.Memory;
|
using ARMeilleure.Memory;
|
||||||
using ARMeilleure.Native;
|
using ARMeilleure.Native;
|
||||||
using Humanizer;
|
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Runtime.Versioning;
|
using System.Runtime.Versioning;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
namespace ARMeilleure.Translation.Cache
|
namespace ARMeilleure.Translation.Cache
|
||||||
{
|
{
|
||||||
public partial class JitCache : IDisposable
|
static partial class JitCache
|
||||||
{
|
{
|
||||||
private static readonly int _pageSize = (int)MemoryBlock.GetPageSize();
|
private static readonly int _pageSize = (int)MemoryBlock.GetPageSize();
|
||||||
private static readonly int _pageMask = _pageSize - 1;
|
private static readonly int _pageMask = _pageSize - 1;
|
||||||
|
|
||||||
private const int CodeAlignment = 4; // Bytes.
|
private const int CodeAlignment = 4;
|
||||||
private const uint CacheSize = 256 * 1024 * 1024;
|
private const int FullCacheSize = 2047 * 1024 * 1024;
|
||||||
|
private const int ReducedCacheSize = FullCacheSize / 8;
|
||||||
|
|
||||||
private readonly JitCacheInvalidation _jitCacheInvalidator;
|
private const float EvictionTargetPercentage = 0.20f;
|
||||||
|
private const int MaxEntriesToEvictAtOnce = 100;
|
||||||
|
|
||||||
private readonly CacheMemoryAllocator _cacheAllocator;
|
// Simple logging configuration
|
||||||
|
private const int LogInterval = 5000; // Log every 5000 allocations
|
||||||
|
|
||||||
private readonly List<CacheEntry> _cacheEntries = [];
|
private static ReservedRegion _jitRegion;
|
||||||
|
private static JitCacheInvalidation _jitCacheInvalidator;
|
||||||
|
|
||||||
private readonly Lock _lock = new();
|
private static CacheMemoryAllocator _cacheAllocator;
|
||||||
|
|
||||||
private readonly List<ReservedRegion> _jitRegions = [];
|
private static readonly List<CacheEntry> _cacheEntries = [];
|
||||||
|
private static readonly Dictionary<int, EntryUsageStats> _entryUsageStats = [];
|
||||||
|
|
||||||
|
private static readonly Lock _lock = new();
|
||||||
|
private static bool _initialized;
|
||||||
|
private static int _cacheSize;
|
||||||
|
|
||||||
|
// Basic statistics
|
||||||
|
private static int _totalAllocations = 0;
|
||||||
|
private static int _totalEvictions = 0;
|
||||||
|
|
||||||
|
private class EntryUsageStats
|
||||||
|
{
|
||||||
|
public long LastAccessTime { get; private set; }
|
||||||
|
public int UsageCount { get; private set; }
|
||||||
|
|
||||||
|
public EntryUsageStats()
|
||||||
|
{
|
||||||
|
LastAccessTime = DateTime.UtcNow.Ticks;
|
||||||
|
UsageCount = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateUsage()
|
||||||
|
{
|
||||||
|
LastAccessTime = DateTime.UtcNow.Ticks;
|
||||||
|
UsageCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[SupportedOSPlatform("windows")]
|
[SupportedOSPlatform("windows")]
|
||||||
[LibraryImport("kernel32.dll", SetLastError = true)]
|
[LibraryImport("kernel32.dll", SetLastError = true)]
|
||||||
private static partial nint FlushInstructionCache(nint hProcess, nint lpAddress, nuint dwSize);
|
public static partial nint FlushInstructionCache(nint hProcess, nint lpAddress, nuint dwSize);
|
||||||
|
|
||||||
public JitCache(IJitMemoryAllocator allocator)
|
public static void Initialize(IJitMemoryAllocator allocator)
|
||||||
{
|
{
|
||||||
|
if (_initialized)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
_jitRegions.Add(new(allocator, CacheSize));
|
if (_initialized)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_cacheAllocator = new((int)CacheSize);
|
_cacheSize = Optimizations.CacheEviction ? ReducedCacheSize : FullCacheSize;
|
||||||
|
_jitRegion = new ReservedRegion(allocator, (ulong)_cacheSize);
|
||||||
|
|
||||||
if (!OperatingSystem.IsWindows() && !OperatingSystem.IsMacOS())
|
if (!OperatingSystem.IsWindows() && !OperatingSystem.IsMacOS())
|
||||||
{
|
{
|
||||||
_jitCacheInvalidator = new JitCacheInvalidation(allocator);
|
_jitCacheInvalidator = new JitCacheInvalidation(allocator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_cacheAllocator = new CacheMemoryAllocator(_cacheSize);
|
||||||
|
|
||||||
if (OperatingSystem.IsWindows())
|
if (OperatingSystem.IsWindows())
|
||||||
{
|
{
|
||||||
JitUnwindWindows.InstallFunctionTableHandler(
|
JitUnwindWindows.InstallFunctionTableHandler(_jitRegion.Pointer, (uint)_cacheSize, _jitRegion.Pointer + Allocate(_pageSize));
|
||||||
this, _jitRegions[0].Pointer, CacheSize, _jitRegions[0].Pointer + Allocate(_pageSize)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Logger.Info?.Print(LogClass.Cpu, $"JIT Cache initialized: Size={_cacheSize / (1024 * 1024)} MB, Eviction={Optimizations.CacheEviction}");
|
||||||
|
_initialized = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public nint Map(CompiledFunction func)
|
public static nint Map(CompiledFunction func)
|
||||||
{
|
{
|
||||||
byte[] code = func.Code;
|
byte[] code = func.Code;
|
||||||
|
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
int funcOffset = Allocate(code.Length);
|
Debug.Assert(_initialized);
|
||||||
nint funcPtr = GetFunctionPtr(funcOffset);
|
_totalAllocations++;
|
||||||
|
|
||||||
|
int funcOffset;
|
||||||
|
|
||||||
|
if (Optimizations.CacheEviction)
|
||||||
|
{
|
||||||
|
int codeSize = AlignCodeSize(code.Length);
|
||||||
|
funcOffset = _cacheAllocator.Allocate(codeSize);
|
||||||
|
|
||||||
|
if (funcOffset < 0)
|
||||||
|
{
|
||||||
|
EvictEntries(codeSize);
|
||||||
|
funcOffset = _cacheAllocator.Allocate(codeSize);
|
||||||
|
|
||||||
|
if (funcOffset < 0)
|
||||||
|
{
|
||||||
|
throw new OutOfMemoryException("JIT Cache exhausted even after eviction.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_jitRegion.ExpandIfNeeded((ulong)funcOffset + (ulong)codeSize);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
funcOffset = Allocate(code.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
nint funcPtr = _jitRegion.Pointer + funcOffset;
|
||||||
|
|
||||||
if (OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
|
if (OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
|
||||||
{
|
{
|
||||||
@@ -95,104 +165,79 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
|
|
||||||
Add(funcOffset, code.Length, func.UnwindInfo);
|
Add(funcOffset, code.Length, func.UnwindInfo);
|
||||||
|
|
||||||
|
// Simple periodic logging
|
||||||
|
if (_totalAllocations % LogInterval == 0)
|
||||||
|
{
|
||||||
|
LogCacheStatus();
|
||||||
|
}
|
||||||
|
|
||||||
return funcPtr;
|
return funcPtr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Unmap(nint pointer)
|
public static void Unmap(nint pointer)
|
||||||
{
|
{
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < _jitRegions.Count; i++)
|
Debug.Assert(_initialized);
|
||||||
|
|
||||||
|
int funcOffset = (int)(pointer.ToInt64() - _jitRegion.Pointer.ToInt64());
|
||||||
|
|
||||||
|
if (TryFind(funcOffset, out CacheEntry entry, out int entryIndex) && entry.Offset == funcOffset)
|
||||||
{
|
{
|
||||||
ReservedRegion jitRegion = _jitRegions[i];
|
_cacheAllocator.Free(funcOffset, AlignCodeSize(entry.Size));
|
||||||
if (pointer.ToInt64() < jitRegion.Pointer.ToInt64() ||
|
_cacheEntries.RemoveAt(entryIndex);
|
||||||
pointer.ToInt64() >= (jitRegion.Pointer + (nint)CacheSize).ToInt64())
|
|
||||||
|
if (Optimizations.CacheEviction)
|
||||||
{
|
{
|
||||||
continue;
|
_entryUsageStats.Remove(funcOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
int funcOffset = (int)(pointer.ToInt64() - jitRegion.Pointer.ToInt64() + i * CacheSize);
|
|
||||||
|
|
||||||
if (TryFind(funcOffset, out CacheEntry entry, out int entryIndex) && entry.Offset == funcOffset)
|
|
||||||
{
|
|
||||||
_cacheAllocator.Free(funcOffset, AlignCodeSize(entry.Size));
|
|
||||||
_cacheEntries.RemoveAt(entryIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ReprotectAsWritable(int offset, int size)
|
private static void ReprotectAsWritable(int offset, int size)
|
||||||
{
|
{
|
||||||
int endOffs = offset + size;
|
int endOffs = offset + size;
|
||||||
int regionStart = (offset % (int)CacheSize) & ~_pageMask;
|
|
||||||
int regionEnd = endOffs % (int)CacheSize == 0
|
int regionStart = offset & ~_pageMask;
|
||||||
? (((int)CacheSize) + _pageMask) & ~_pageMask
|
int regionEnd = (endOffs + _pageMask) & ~_pageMask;
|
||||||
: ((endOffs % (int)CacheSize) + _pageMask) & ~_pageMask;
|
|
||||||
GetRegion(offset).Block.MapAsRwx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
_jitRegion.Block.MapAsRwx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ReprotectAsExecutable(int offset, int size)
|
private static void ReprotectAsExecutable(int offset, int size)
|
||||||
{
|
{
|
||||||
int endOffs = offset + size;
|
int endOffs = offset + size;
|
||||||
int regionStart = (offset % (int)CacheSize) & ~_pageMask;
|
|
||||||
int regionEnd = endOffs % (int)CacheSize == 0
|
|
||||||
? (((int)CacheSize) + _pageMask) & ~_pageMask
|
|
||||||
: ((endOffs % (int)CacheSize) + _pageMask) & ~_pageMask;
|
|
||||||
|
|
||||||
GetRegion(offset).Block.MapAsRx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
int regionStart = offset & ~_pageMask;
|
||||||
|
int regionEnd = (endOffs + _pageMask) & ~_pageMask;
|
||||||
|
|
||||||
|
_jitRegion.Block.MapAsRx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
||||||
}
|
}
|
||||||
|
|
||||||
private int Allocate(int codeSize)
|
private static int Allocate(int codeSize)
|
||||||
{
|
{
|
||||||
codeSize = AlignCodeSize(codeSize);
|
codeSize = AlignCodeSize(codeSize);
|
||||||
|
|
||||||
int allocOffset = _cacheAllocator.Allocate(codeSize);
|
int allocOffset = _cacheAllocator.Allocate(codeSize);
|
||||||
|
|
||||||
if (allocOffset >= 0)
|
|
||||||
{
|
|
||||||
GetRegion(allocOffset).ExpandIfNeeded((ulong)(allocOffset % (int)CacheSize) + (ulong)codeSize);
|
|
||||||
return allocOffset;
|
|
||||||
}
|
|
||||||
|
|
||||||
_cacheAllocator.AddNewBlocks(1);
|
|
||||||
ReservedRegion newRegion = new(_jitRegions[0].Allocator, CacheSize);
|
|
||||||
|
|
||||||
Logger.Warning?.Print(LogClass.Cpu, $"JIT Cache of size {(_jitRegions.Count * CacheSize).Bytes()} exhausted, creating new Cache Region ({((_jitRegions.Count + 1) * CacheSize).Bytes()} Total Allocation).");
|
|
||||||
|
|
||||||
_jitRegions.Add(newRegion);
|
|
||||||
|
|
||||||
allocOffset = _cacheAllocator.Allocate(codeSize);
|
|
||||||
if (allocOffset < 0)
|
if (allocOffset < 0)
|
||||||
{
|
{
|
||||||
throw new OutOfMemoryException("Failed to allocate in new Cache Region!");
|
throw new OutOfMemoryException("JIT Cache exhausted.");
|
||||||
}
|
}
|
||||||
|
|
||||||
GetRegion(allocOffset).ExpandIfNeeded((ulong)(allocOffset % (int)CacheSize) + (ulong)codeSize);
|
_jitRegion.ExpandIfNeeded((ulong)allocOffset + (ulong)codeSize);
|
||||||
|
|
||||||
return allocOffset;
|
return allocOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
private nint GetFunctionPtr(int offset)
|
|
||||||
{
|
|
||||||
return GetRegion(offset).Pointer + (offset % (int)CacheSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ReservedRegion GetRegion(int offset)
|
|
||||||
{
|
|
||||||
int index = offset / (int)CacheSize;
|
|
||||||
|
|
||||||
return _jitRegions[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int AlignCodeSize(int codeSize)
|
private static int AlignCodeSize(int codeSize)
|
||||||
{
|
{
|
||||||
return checked(codeSize + (CodeAlignment - 1)) & ~(CodeAlignment - 1);
|
return checked(codeSize + (CodeAlignment - 1)) & ~(CodeAlignment - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Add(int offset, int size, UnwindInfo unwindInfo)
|
private static void Add(int offset, int size, UnwindInfo unwindInfo)
|
||||||
{
|
{
|
||||||
CacheEntry entry = new(offset, size, unwindInfo);
|
CacheEntry entry = new(offset, size, unwindInfo);
|
||||||
|
|
||||||
@@ -204,9 +249,14 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
}
|
}
|
||||||
|
|
||||||
_cacheEntries.Insert(index, entry);
|
_cacheEntries.Insert(index, entry);
|
||||||
|
|
||||||
|
if (Optimizations.CacheEviction)
|
||||||
|
{
|
||||||
|
_entryUsageStats[offset] = new EntryUsageStats();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryFind(int offset, out CacheEntry entry, out int entryIndex)
|
public static bool TryFind(int offset, out CacheEntry entry, out int entryIndex)
|
||||||
{
|
{
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
@@ -220,6 +270,12 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
if (index >= 0)
|
if (index >= 0)
|
||||||
{
|
{
|
||||||
entry = _cacheEntries[index];
|
entry = _cacheEntries[index];
|
||||||
|
|
||||||
|
if (Optimizations.CacheEviction && _entryUsageStats.TryGetValue(offset, out EntryUsageStats stats))
|
||||||
|
{
|
||||||
|
stats.UpdateUsage();
|
||||||
|
}
|
||||||
|
|
||||||
entryIndex = index;
|
entryIndex = index;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -230,17 +286,82 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
private static void EvictEntries(int requiredSize)
|
||||||
{
|
{
|
||||||
if (OperatingSystem.IsWindows())
|
if (!Optimizations.CacheEviction)
|
||||||
{
|
{
|
||||||
JitUnwindWindows.RemoveFunctionTableHandler(_jitRegions[0].Pointer);
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (ReservedRegion jitRegion in _jitRegions)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
jitRegion.Dispose();
|
int targetSpace = Math.Max(requiredSize, (int)(_cacheSize * EvictionTargetPercentage));
|
||||||
|
int freedSpace = 0;
|
||||||
|
int evictedCount = 0;
|
||||||
|
|
||||||
|
var entriesWithStats = _cacheEntries
|
||||||
|
.Where(e => _entryUsageStats.ContainsKey(e.Offset))
|
||||||
|
.Select(e => new {
|
||||||
|
Entry = e,
|
||||||
|
Stats = _entryUsageStats[e.Offset],
|
||||||
|
Score = CalculateEvictionScore(_entryUsageStats[e.Offset])
|
||||||
|
})
|
||||||
|
.OrderBy(x => x.Score)
|
||||||
|
.Take(MaxEntriesToEvictAtOnce)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
foreach (var item in entriesWithStats)
|
||||||
|
{
|
||||||
|
int entrySize = AlignCodeSize(item.Entry.Size);
|
||||||
|
|
||||||
|
int entryIndex = _cacheEntries.BinarySearch(item.Entry);
|
||||||
|
if (entryIndex >= 0)
|
||||||
|
{
|
||||||
|
_cacheAllocator.Free(item.Entry.Offset, entrySize);
|
||||||
|
_cacheEntries.RemoveAt(entryIndex);
|
||||||
|
_entryUsageStats.Remove(item.Entry.Offset);
|
||||||
|
|
||||||
|
freedSpace += entrySize;
|
||||||
|
evictedCount++;
|
||||||
|
|
||||||
|
if (freedSpace >= targetSpace)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_totalEvictions += evictedCount;
|
||||||
|
|
||||||
|
Logger.Info?.Print(LogClass.Cpu, $"JIT Cache: Evicted {evictedCount} entries, freed {freedSpace / (1024 * 1024.0):F2} MB");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static double CalculateEvictionScore(EntryUsageStats stats)
|
||||||
|
{
|
||||||
|
long currentTime = DateTime.UtcNow.Ticks;
|
||||||
|
long ageInTicks = currentTime - stats.LastAccessTime;
|
||||||
|
|
||||||
|
double ageInSeconds = ageInTicks / 10_000_000.0;
|
||||||
|
|
||||||
|
const double usageWeight = 1.0;
|
||||||
|
const double ageWeight = 2.0;
|
||||||
|
|
||||||
|
double usageScore = Math.Log10(stats.UsageCount + 1) * usageWeight;
|
||||||
|
double ageScore = (10.0 / (ageInSeconds + 1.0)) * ageWeight;
|
||||||
|
|
||||||
|
return usageScore + ageScore;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void LogCacheStatus()
|
||||||
|
{
|
||||||
|
int estimatedUsedSize = _cacheEntries.Sum(e => AlignCodeSize(e.Size));
|
||||||
|
double usagePercentage = 100.0 * estimatedUsedSize / _cacheSize;
|
||||||
|
|
||||||
|
Logger.Info?.Print(LogClass.Cpu,
|
||||||
|
$"JIT Cache status: entries={_cacheEntries.Count}, " +
|
||||||
|
$"est. used={estimatedUsedSize / (1024 * 1024.0):F2} MB ({usagePercentage:F1}%), " +
|
||||||
|
$"evictions={_totalEvictions}, allocations={_totalAllocations}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,6 @@
|
|||||||
|
|
||||||
using ARMeilleure.CodeGen.Unwinding;
|
using ARMeilleure.CodeGen.Unwinding;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
@@ -29,29 +27,6 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
public unsafe fixed ushort UnwindCodes[MaxUnwindCodesArraySize];
|
public unsafe fixed ushort UnwindCodes[MaxUnwindCodesArraySize];
|
||||||
}
|
}
|
||||||
|
|
||||||
private unsafe struct InternalFunctionHandler
|
|
||||||
{
|
|
||||||
public InternalFunctionHandler(JitCache jitCache, nint workBufferPtr)
|
|
||||||
{
|
|
||||||
_jitCache = jitCache;
|
|
||||||
|
|
||||||
_runtimeFunction = (RuntimeFunction*)workBufferPtr;
|
|
||||||
|
|
||||||
_unwindInfo = (UnwindInfo*)(workBufferPtr + _sizeOfRuntimeFunction);
|
|
||||||
}
|
|
||||||
|
|
||||||
readonly JitCache _jitCache;
|
|
||||||
|
|
||||||
readonly RuntimeFunction* _runtimeFunction;
|
|
||||||
|
|
||||||
readonly UnwindInfo* _unwindInfo;
|
|
||||||
|
|
||||||
public RuntimeFunction* FunctionTableHandler(ulong controlPc, nint context)
|
|
||||||
{
|
|
||||||
return JitUnwindWindows.FunctionTableHandler(_jitCache, _runtimeFunction, _unwindInfo, controlPc, context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum UnwindOp
|
private enum UnwindOp
|
||||||
{
|
{
|
||||||
PushNonvol = 0,
|
PushNonvol = 0,
|
||||||
@@ -84,28 +59,27 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
|
|
||||||
private static GetRuntimeFunctionCallback _getRuntimeFunctionCallback;
|
private static GetRuntimeFunctionCallback _getRuntimeFunctionCallback;
|
||||||
|
|
||||||
private static readonly int _sizeOfRuntimeFunction;
|
private static int _sizeOfRuntimeFunction;
|
||||||
|
|
||||||
private static readonly ConcurrentDictionary<ulong, InternalFunctionHandler> _functionTableHandlers = new();
|
private unsafe static RuntimeFunction* _runtimeFunction;
|
||||||
|
|
||||||
static JitUnwindWindows()
|
private unsafe static UnwindInfo* _unwindInfo;
|
||||||
{
|
|
||||||
_sizeOfRuntimeFunction = Marshal.SizeOf<RuntimeFunction>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void InstallFunctionTableHandler(JitCache jitCache, nint codeCachePointer, uint codeCacheLength, nint workBufferPtr)
|
public static void InstallFunctionTableHandler(nint codeCachePointer, uint codeCacheLength, nint workBufferPtr)
|
||||||
{
|
{
|
||||||
ulong codeCachePtr = (ulong)codeCachePointer.ToInt64();
|
ulong codeCachePtr = (ulong)codeCachePointer.ToInt64();
|
||||||
|
|
||||||
bool result;
|
_sizeOfRuntimeFunction = Marshal.SizeOf<RuntimeFunction>();
|
||||||
|
|
||||||
InternalFunctionHandler handler;
|
bool result;
|
||||||
|
|
||||||
unsafe
|
unsafe
|
||||||
{
|
{
|
||||||
handler = new InternalFunctionHandler(jitCache, workBufferPtr);
|
_runtimeFunction = (RuntimeFunction*)workBufferPtr;
|
||||||
|
|
||||||
_getRuntimeFunctionCallback = handler.FunctionTableHandler;
|
_unwindInfo = (UnwindInfo*)(workBufferPtr + _sizeOfRuntimeFunction);
|
||||||
|
|
||||||
|
_getRuntimeFunctionCallback = new GetRuntimeFunctionCallback(FunctionTableHandler);
|
||||||
|
|
||||||
result = RtlInstallFunctionTableCallback(
|
result = RtlInstallFunctionTableCallback(
|
||||||
codeCachePtr | 3,
|
codeCachePtr | 3,
|
||||||
@@ -120,8 +94,6 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
{
|
{
|
||||||
throw new InvalidOperationException("Failure installing function table callback.");
|
throw new InvalidOperationException("Failure installing function table callback.");
|
||||||
}
|
}
|
||||||
|
|
||||||
_functionTableHandlers.TryAdd(codeCachePtr, handler);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void RemoveFunctionTableHandler(nint codeCachePointer)
|
public static void RemoveFunctionTableHandler(nint codeCachePointer)
|
||||||
@@ -139,26 +111,24 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
{
|
{
|
||||||
throw new InvalidOperationException("Failure removing function table callback.");
|
throw new InvalidOperationException("Failure removing function table callback.");
|
||||||
}
|
}
|
||||||
|
|
||||||
_functionTableHandlers.Remove(codeCachePtr, out _);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static unsafe RuntimeFunction* FunctionTableHandler(JitCache jitCache, RuntimeFunction* runtimeFunction, UnwindInfo* unwindInfo, ulong controlPc, nint context)
|
private static unsafe RuntimeFunction* FunctionTableHandler(ulong controlPc, nint context)
|
||||||
{
|
{
|
||||||
int offset = (int)((long)controlPc - context.ToInt64());
|
int offset = (int)((long)controlPc - context.ToInt64());
|
||||||
|
|
||||||
if (!jitCache.TryFind(offset, out CacheEntry funcEntry, out _))
|
if (!JitCache.TryFind(offset, out CacheEntry funcEntry, out _))
|
||||||
{
|
{
|
||||||
return null; // Not found.
|
return null; // Not found.
|
||||||
}
|
}
|
||||||
|
|
||||||
CodeGen.Unwinding.UnwindInfo funcUnwindInfo = funcEntry.UnwindInfo;
|
CodeGen.Unwinding.UnwindInfo unwindInfo = funcEntry.UnwindInfo;
|
||||||
|
|
||||||
int codeIndex = 0;
|
int codeIndex = 0;
|
||||||
|
|
||||||
for (int index = funcUnwindInfo.PushEntries.Length - 1; index >= 0; index--)
|
for (int index = unwindInfo.PushEntries.Length - 1; index >= 0; index--)
|
||||||
{
|
{
|
||||||
UnwindPushEntry entry = funcUnwindInfo.PushEntries[index];
|
UnwindPushEntry entry = unwindInfo.PushEntries[index];
|
||||||
|
|
||||||
switch (entry.PseudoOp)
|
switch (entry.PseudoOp)
|
||||||
{
|
{
|
||||||
@@ -170,14 +140,14 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
|
|
||||||
if (stackOffset <= 0xFFFF0)
|
if (stackOffset <= 0xFFFF0)
|
||||||
{
|
{
|
||||||
unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOp.SaveXmm128, entry.PrologOffset, entry.RegIndex);
|
_unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOp.SaveXmm128, entry.PrologOffset, entry.RegIndex);
|
||||||
unwindInfo->UnwindCodes[codeIndex++] = (ushort)(stackOffset / 16);
|
_unwindInfo->UnwindCodes[codeIndex++] = (ushort)(stackOffset / 16);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOp.SaveXmm128Far, entry.PrologOffset, entry.RegIndex);
|
_unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOp.SaveXmm128Far, entry.PrologOffset, entry.RegIndex);
|
||||||
unwindInfo->UnwindCodes[codeIndex++] = (ushort)(stackOffset >> 0);
|
_unwindInfo->UnwindCodes[codeIndex++] = (ushort)(stackOffset >> 0);
|
||||||
unwindInfo->UnwindCodes[codeIndex++] = (ushort)(stackOffset >> 16);
|
_unwindInfo->UnwindCodes[codeIndex++] = (ushort)(stackOffset >> 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -191,18 +161,18 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
|
|
||||||
if (allocSize <= 128)
|
if (allocSize <= 128)
|
||||||
{
|
{
|
||||||
unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOp.AllocSmall, entry.PrologOffset, (allocSize / 8) - 1);
|
_unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOp.AllocSmall, entry.PrologOffset, (allocSize / 8) - 1);
|
||||||
}
|
}
|
||||||
else if (allocSize <= 0x7FFF8)
|
else if (allocSize <= 0x7FFF8)
|
||||||
{
|
{
|
||||||
unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOp.AllocLarge, entry.PrologOffset, 0);
|
_unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOp.AllocLarge, entry.PrologOffset, 0);
|
||||||
unwindInfo->UnwindCodes[codeIndex++] = (ushort)(allocSize / 8);
|
_unwindInfo->UnwindCodes[codeIndex++] = (ushort)(allocSize / 8);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOp.AllocLarge, entry.PrologOffset, 1);
|
_unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOp.AllocLarge, entry.PrologOffset, 1);
|
||||||
unwindInfo->UnwindCodes[codeIndex++] = (ushort)(allocSize >> 0);
|
_unwindInfo->UnwindCodes[codeIndex++] = (ushort)(allocSize >> 0);
|
||||||
unwindInfo->UnwindCodes[codeIndex++] = (ushort)(allocSize >> 16);
|
_unwindInfo->UnwindCodes[codeIndex++] = (ushort)(allocSize >> 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -210,7 +180,7 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
|
|
||||||
case UnwindPseudoOp.PushReg:
|
case UnwindPseudoOp.PushReg:
|
||||||
{
|
{
|
||||||
unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOp.PushNonvol, entry.PrologOffset, entry.RegIndex);
|
_unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOp.PushNonvol, entry.PrologOffset, entry.RegIndex);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -222,16 +192,16 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
|
|
||||||
Debug.Assert(codeIndex <= MaxUnwindCodesArraySize);
|
Debug.Assert(codeIndex <= MaxUnwindCodesArraySize);
|
||||||
|
|
||||||
unwindInfo->VersionAndFlags = 1; // Flags: The function has no handler.
|
_unwindInfo->VersionAndFlags = 1; // Flags: The function has no handler.
|
||||||
unwindInfo->SizeOfProlog = (byte)funcUnwindInfo.PrologSize;
|
_unwindInfo->SizeOfProlog = (byte)unwindInfo.PrologSize;
|
||||||
unwindInfo->CountOfUnwindCodes = (byte)codeIndex;
|
_unwindInfo->CountOfUnwindCodes = (byte)codeIndex;
|
||||||
unwindInfo->FrameRegister = 0;
|
_unwindInfo->FrameRegister = 0;
|
||||||
|
|
||||||
runtimeFunction->BeginAddress = (uint)funcEntry.Offset;
|
_runtimeFunction->BeginAddress = (uint)funcEntry.Offset;
|
||||||
runtimeFunction->EndAddress = (uint)(funcEntry.Offset + funcEntry.Size);
|
_runtimeFunction->EndAddress = (uint)(funcEntry.Offset + funcEntry.Size);
|
||||||
runtimeFunction->UnwindData = (uint)_sizeOfRuntimeFunction;
|
_runtimeFunction->UnwindData = (uint)_sizeOfRuntimeFunction;
|
||||||
|
|
||||||
return runtimeFunction;
|
return _runtimeFunction;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ushort PackUnwindOp(UnwindOp op, int prologOffset, int opInfo)
|
private static ushort PackUnwindOp(UnwindOp op, int prologOffset, int opInfo)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
namespace ARMeilleure.Translation
|
namespace ARMeilleure.Translation
|
||||||
{
|
{
|
||||||
class DelegateInfo
|
public class DelegateInfo
|
||||||
{
|
{
|
||||||
public nint FuncPtr { get; }
|
public nint FuncPtr { get; }
|
||||||
|
|
||||||
|
|||||||
@@ -2,11 +2,358 @@ using ARMeilleure.Instructions;
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
#if ANDROID
|
||||||
|
using ARMeilleure.State;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace ARMeilleure.Translation
|
namespace ARMeilleure.Translation
|
||||||
{
|
{
|
||||||
static class Delegates
|
static class Delegates
|
||||||
{
|
{
|
||||||
|
#if ANDROID
|
||||||
|
// MathHelper delegates
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate double MathHelperAbsDelegate(double value);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate double MathHelperCeilingDelegate(double value);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate double MathHelperFloorDelegate(double value);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate double MathHelperRoundDelegate(double value, int mode);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate double MathHelperTruncateDelegate(double value);
|
||||||
|
|
||||||
|
// MathHelperF delegates
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate float MathHelperFAbsDelegate(float value);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate float MathHelperFCeilingDelegate(float value);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate float MathHelperFFloorDelegate(float value);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate float MathHelperFRoundDelegate(float value, int mode);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate float MathHelperFTruncateDelegate(float value);
|
||||||
|
|
||||||
|
// NativeInterface delegates
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate void NativeInterfaceBreakDelegate(ulong address, int imm);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate byte NativeInterfaceCheckSynchronizationDelegate();
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate void NativeInterfaceEnqueueForRejitDelegate(ulong address);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate ulong NativeInterfaceGetCntfrqEl0Delegate();
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate ulong NativeInterfaceGetCntpctEl0Delegate();
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate ulong NativeInterfaceGetCntvctEl0Delegate();
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate ulong NativeInterfaceGetCtrEl0Delegate();
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate ulong NativeInterfaceGetDczidEl0Delegate();
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate ulong NativeInterfaceGetFunctionAddressDelegate(ulong address);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate void NativeInterfaceInvalidateCacheLineDelegate(ulong address);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate byte NativeInterfaceReadByteDelegate(ulong address);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate ushort NativeInterfaceReadUInt16Delegate(ulong address);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate uint NativeInterfaceReadUInt32Delegate(ulong address);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate ulong NativeInterfaceReadUInt64Delegate(ulong address);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate V128 NativeInterfaceReadVector128Delegate(ulong address);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate void NativeInterfaceSignalMemoryTrackingDelegate(ulong address, ulong size, byte write);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate void NativeInterfaceSupervisorCallDelegate(ulong address, int imm);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate void NativeInterfaceThrowInvalidMemoryAccessDelegate(ulong address);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate void NativeInterfaceUndefinedDelegate(ulong address, int opCode);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate void NativeInterfaceWriteByteDelegate(ulong address, byte value);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate void NativeInterfaceWriteUInt16Delegate(ulong address, ushort value);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate void NativeInterfaceWriteUInt32Delegate(ulong address, uint value);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate void NativeInterfaceWriteUInt64Delegate(ulong address, ulong value);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate void NativeInterfaceWriteVector128Delegate(ulong address, V128 value);
|
||||||
|
|
||||||
|
// SoftFallback delegates
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate ulong SoftFallbackCountLeadingSignsDelegate(ulong value, int size);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate ulong SoftFallbackCountLeadingZerosDelegate(ulong value, int size);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate uint SoftFallbackCrc32bDelegate(uint crc, byte val);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate uint SoftFallbackCrc32cbDelegate(uint crc, byte val);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate uint SoftFallbackCrc32chDelegate(uint crc, ushort val);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate uint SoftFallbackCrc32cwDelegate(uint crc, uint val);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate uint SoftFallbackCrc32cxDelegate(uint crc, ulong val);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate uint SoftFallbackCrc32hDelegate(uint crc, ushort val);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate uint SoftFallbackCrc32wDelegate(uint crc, uint val);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate uint SoftFallbackCrc32xDelegate(uint crc, ulong val);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate V128 SoftFallbackDecryptDelegate(V128 value, V128 roundKey);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate V128 SoftFallbackEncryptDelegate(V128 value, V128 roundKey);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate uint SoftFallbackFixedRotateDelegate(uint hash_e);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate V128 SoftFallbackHashChooseDelegate(V128 hash_abcd, uint hash_e, V128 wk);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate V128 SoftFallbackHashLowerDelegate(V128 hash_abcd, V128 hash_efgh, V128 wk);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate V128 SoftFallbackHashMajorityDelegate(V128 hash_abcd, uint hash_e, V128 wk);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate V128 SoftFallbackHashParityDelegate(V128 hash_abcd, uint hash_e, V128 wk);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate V128 SoftFallbackHashUpperDelegate(V128 hash_abcd, V128 hash_efgh, V128 wk);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate V128 SoftFallbackInverseMixColumnsDelegate(V128 value);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate V128 SoftFallbackMixColumnsDelegate(V128 value);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate V128 SoftFallbackPolynomialMult64_128Delegate(ulong op1, ulong op2);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate int SoftFallbackSatF32ToS32Delegate(float value);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate long SoftFallbackSatF32ToS64Delegate(float value);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate uint SoftFallbackSatF32ToU32Delegate(float value);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate ulong SoftFallbackSatF32ToU64Delegate(float value);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate int SoftFallbackSatF64ToS32Delegate(double value);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate long SoftFallbackSatF64ToS64Delegate(double value);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate uint SoftFallbackSatF64ToU32Delegate(double value);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate ulong SoftFallbackSatF64ToU64Delegate(double value);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate V128 SoftFallbackSha1SchedulePart1Delegate(V128 w0_3, V128 w4_7, V128 w8_11);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate V128 SoftFallbackSha1SchedulePart2Delegate(V128 tw0_3, V128 w12_15);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate V128 SoftFallbackSha256SchedulePart1Delegate(V128 w0_3, V128 w4_7);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate V128 SoftFallbackSha256SchedulePart2Delegate(V128 w0_3, V128 w8_11, V128 w12_15);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate long SoftFallbackSignedShrImm64Delegate(long value, long roundConst, int shift);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate V128 SoftFallbackTbl1Delegate(V128 vector, int bytes, V128 table);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate V128 SoftFallbackTbl2Delegate(V128 vector, int bytes, V128 table0, V128 table1);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate V128 SoftFallbackTbl3Delegate(V128 vector, int bytes, V128 table0, V128 table1, V128 table2);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate V128 SoftFallbackTbl4Delegate(V128 vector, int bytes, V128 table0, V128 table1, V128 table2, V128 table3);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate V128 SoftFallbackTbx1Delegate(V128 dest, V128 vector, int bytes, V128 table);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate V128 SoftFallbackTbx2Delegate(V128 dest, V128 vector, int bytes, V128 table0, V128 table1);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate V128 SoftFallbackTbx3Delegate(V128 dest, V128 vector, int bytes, V128 table0, V128 table1, V128 table2);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate V128 SoftFallbackTbx4Delegate(V128 dest, V128 vector, int bytes, V128 table0, V128 table1, V128 table2, V128 table3);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate ulong SoftFallbackUnsignedShrImm64Delegate(ulong value, long roundConst, int shift);
|
||||||
|
|
||||||
|
// SoftFloat delegates
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate float SoftFloat16_32FPConvertDelegate(ushort value);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate double SoftFloat16_64FPConvertDelegate(ushort value);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate float SoftFloat32FPAddDelegate(float a, float b);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate float SoftFloat32FPAddFpscrDelegate(float a, float b, byte standardFpscr);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate int SoftFloat32FPCompareDelegate(float a, float b, byte signalNaNs);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate float SoftFloat32FPCompareEQDelegate(float a, float b);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate float SoftFloat32FPCompareEQFpscrDelegate(float a, float b, byte standardFpscr);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate float SoftFloat32FPCompareGEDelegate(float a, float b);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate float SoftFloat32FPCompareGEFpscrDelegate(float a, float b, byte standardFpscr);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate float SoftFloat32FPCompareGTDelegate(float a, float b);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate float SoftFloat32FPCompareGTFpscrDelegate(float a, float b, byte standardFpscr);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate float SoftFloat32FPCompareLEDelegate(float a, float b);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate float SoftFloat32FPCompareLEFpscrDelegate(float a, float b, byte standardFpscr);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate float SoftFloat32FPCompareLTDelegate(float a, float b);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate float SoftFloat32FPCompareLTFpscrDelegate(float a, float b, byte standardFpscr);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate float SoftFloat32FPDivDelegate(float a, float b);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate float SoftFloat32FPMaxDelegate(float a, float b);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate float SoftFloat32FPMaxFpscrDelegate(float a, float b, byte standardFpscr);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate float SoftFloat32FPMaxNumDelegate(float a, float b);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate float SoftFloat32FPMaxNumFpscrDelegate(float a, float b, byte standardFpscr);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate float SoftFloat32FPMinDelegate(float a, float b);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate float SoftFloat32FPMinFpscrDelegate(float a, float b, byte standardFpscr);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate float SoftFloat32FPMinNumDelegate(float a, float b);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate float SoftFloat32FPMinNumFpscrDelegate(float a, float b, byte standardFpscr);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate float SoftFloat32FPMulDelegate(float a, float b);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate float SoftFloat32FPMulFpscrDelegate(float a, float b, byte standardFpscr);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate float SoftFloat32FPMulAddDelegate(float a, float b, float c);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate float SoftFloat32FPMulAddFpscrDelegate(float a, float b, float c, byte standardFpscr);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate float SoftFloat32FPMulSubDelegate(float a, float b, float c);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate float SoftFloat32FPMulSubFpscrDelegate(float a, float b, float c, byte standardFpscr);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate float SoftFloat32FPMulXDelegate(float a, float b);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate float SoftFloat32FPNegMulAddDelegate(float a, float b, float c);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate float SoftFloat32FPNegMulSubDelegate(float a, float b, float c);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate float SoftFloat32FPRecipEstimateDelegate(float a);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate float SoftFloat32FPRecipEstimateFpscrDelegate(float a, byte standardFpscr);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate float SoftFloat32FPRecipStepDelegate(float a, float b);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate float SoftFloat32FPRecipStepFusedDelegate(float a, float b);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate float SoftFloat32FPRecpXDelegate(float a);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate float SoftFloat32FPRSqrtEstimateDelegate(float a);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate float SoftFloat32FPRSqrtEstimateFpscrDelegate(float a, byte standardFpscr);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate float SoftFloat32FPRSqrtStepDelegate(float a, float b);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate float SoftFloat32FPRSqrtStepFusedDelegate(float a, float b);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate float SoftFloat32FPSqrtDelegate(float a);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate float SoftFloat32FPSubDelegate(float a, float b);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate ushort SoftFloat32_16FPConvertDelegate(float value);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate double SoftFloat64FPAddDelegate(double a, double b);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate double SoftFloat64FPAddFpscrDelegate(double a, double b, byte standardFpscr);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate int SoftFloat64FPCompareDelegate(double a, double b, byte signalNaNs);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate double SoftFloat64FPCompareEQDelegate(double a, double b);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate double SoftFloat64FPCompareEQFpscrDelegate(double a, double b, byte standardFpscr);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate double SoftFloat64FPCompareGEDelegate(double a, double b);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate double SoftFloat64FPCompareGEFpscrDelegate(double a, double b, byte standardFpscr);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate double SoftFloat64FPCompareGTDelegate(double a, double b);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate double SoftFloat64FPCompareGTFpscrDelegate(double a, double b, byte standardFpscr);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate double SoftFloat64FPCompareLEDelegate(double a, double b);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate double SoftFloat64FPCompareLEFpscrDelegate(double a, double b, byte standardFpscr);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate double SoftFloat64FPCompareLTDelegate(double a, double b);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate double SoftFloat64FPCompareLTFpscrDelegate(double a, double b, byte standardFpscr);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate double SoftFloat64FPDivDelegate(double a, double b);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate double SoftFloat64FPMaxDelegate(double a, double b);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate double SoftFloat64FPMaxFpscrDelegate(double a, double b, byte standardFpscr);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate double SoftFloat64FPMaxNumDelegate(double a, double b);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate double SoftFloat64FPMaxNumFpscrDelegate(double a, double b, byte standardFpscr);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate double SoftFloat64FPMinDelegate(double a, double b);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate double SoftFloat64FPMinFpscrDelegate(double a, double b, byte standardFpscr);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate double SoftFloat64FPMinNumDelegate(double a, double b);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate double SoftFloat64FPMinNumFpscrDelegate(double a, double b, byte standardFpscr);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate double SoftFloat64FPMulDelegate(double a, double b);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate double SoftFloat64FPMulFpscrDelegate(double a, double b, byte standardFpscr);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate double SoftFloat64FPMulAddDelegate(double a, double b, double c);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate double SoftFloat64FPMulAddFpscrDelegate(double a, double b, double c, byte standardFpscr);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate double SoftFloat64FPMulSubDelegate(double a, double b, double c);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate double SoftFloat64FPMulSubFpscrDelegate(double a, double b, double c, byte standardFpscr);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate double SoftFloat64FPMulXDelegate(double a, double b);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate double SoftFloat64FPNegMulAddDelegate(double a, double b, double c);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate double SoftFloat64FPNegMulSubDelegate(double a, double b, double c);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate double SoftFloat64FPRecipEstimateDelegate(double a);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate double SoftFloat64FPRecipEstimateFpscrDelegate(double a, byte standardFpscr);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate double SoftFloat64FPRecipStepDelegate(double a, double b);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate double SoftFloat64FPRecipStepFusedDelegate(double a, double b);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate double SoftFloat64FPRecpXDelegate(double a);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate double SoftFloat64FPRSqrtEstimateDelegate(double a);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate double SoftFloat64FPRSqrtEstimateFpscrDelegate(double a, byte standardFpscr);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate double SoftFloat64FPRSqrtStepDelegate(double a, double b);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate double SoftFloat64FPRSqrtStepFusedDelegate(double a, double b);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate double SoftFloat64FPSqrtDelegate(double a);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate double SoftFloat64FPSubDelegate(double a, double b);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate ushort SoftFloat64_16FPConvertDelegate(double value);
|
||||||
|
|
||||||
|
private static readonly Dictionary<string, Delegate> _androidDelegates;
|
||||||
|
#endif
|
||||||
public static bool TryGetDelegateFuncPtrByIndex(int index, out nint funcPtr)
|
public static bool TryGetDelegateFuncPtrByIndex(int index, out nint funcPtr)
|
||||||
{
|
{
|
||||||
if (index >= 0 && index < _delegates.Count)
|
if (index >= 0 && index < _delegates.Count)
|
||||||
@@ -53,7 +400,32 @@ namespace ARMeilleure.Translation
|
|||||||
{
|
{
|
||||||
string key = GetKey(method);
|
string key = GetKey(method);
|
||||||
|
|
||||||
_delegates.Add(key, new DelegateInfo(method.MethodHandle.GetFunctionPointer())); // ArgumentException (key).
|
#if ANDROID
|
||||||
|
if (_androidDelegates.TryGetValue(key, out Delegate del))
|
||||||
|
{
|
||||||
|
nint funcPtr = Marshal.GetFunctionPointerForDelegate(del);
|
||||||
|
_delegates.Add(key, new DelegateInfo(funcPtr));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_delegates.Add(key, new DelegateInfo(method.MethodHandle.GetFunctionPointer()));
|
||||||
|
}
|
||||||
|
catch (NotSupportedException) when (IsRunningOnAndroid())
|
||||||
|
{
|
||||||
|
throw new PlatformNotSupportedException($"Cannot obtain function pointer {key}: it must be registered.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsRunningOnAndroid()
|
||||||
|
{
|
||||||
|
#if ANDROID
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetKey(MethodInfo info)
|
private static string GetKey(MethodInfo info)
|
||||||
@@ -67,6 +439,190 @@ namespace ARMeilleure.Translation
|
|||||||
{
|
{
|
||||||
_delegates = new SortedList<string, DelegateInfo>();
|
_delegates = new SortedList<string, DelegateInfo>();
|
||||||
|
|
||||||
|
#if ANDROID
|
||||||
|
_androidDelegates = new Dictionary<string, Delegate>
|
||||||
|
{
|
||||||
|
// MathHelper delegates
|
||||||
|
{ "MathHelper.Abs", new MathHelperAbsDelegate(Math.Abs) },
|
||||||
|
{ "MathHelper.Ceiling", new MathHelperCeilingDelegate(Math.Ceiling) },
|
||||||
|
{ "MathHelper.Floor", new MathHelperFloorDelegate(Math.Floor) },
|
||||||
|
{ "MathHelper.Round", new MathHelperRoundDelegate((value, mode) => Math.Round(value, (MidpointRounding)mode)) },
|
||||||
|
{ "MathHelper.Truncate", new MathHelperTruncateDelegate(Math.Truncate) },
|
||||||
|
|
||||||
|
// MathHelperF delegates
|
||||||
|
{ "MathHelperF.Abs", new MathHelperFAbsDelegate(MathF.Abs) },
|
||||||
|
{ "MathHelperF.Ceiling", new MathHelperFCeilingDelegate(MathF.Ceiling) },
|
||||||
|
{ "MathHelperF.Floor", new MathHelperFFloorDelegate(MathF.Floor) },
|
||||||
|
{ "MathHelperF.Round", new MathHelperFRoundDelegate((value, mode) => MathF.Round(value, (MidpointRounding)mode)) },
|
||||||
|
{ "MathHelperF.Truncate", new MathHelperFTruncateDelegate(MathF.Truncate) },
|
||||||
|
|
||||||
|
// NativeInterface delegates
|
||||||
|
{ "NativeInterface.Break", new NativeInterfaceBreakDelegate(NativeInterface.Break) },
|
||||||
|
{ "NativeInterface.CheckSynchronization", new NativeInterfaceCheckSynchronizationDelegate(NativeInterface.CheckSynchronization) },
|
||||||
|
{ "NativeInterface.EnqueueForRejit", new NativeInterfaceEnqueueForRejitDelegate(NativeInterface.EnqueueForRejit) },
|
||||||
|
{ "NativeInterface.GetCntfrqEl0", new NativeInterfaceGetCntfrqEl0Delegate(NativeInterface.GetCntfrqEl0) },
|
||||||
|
{ "NativeInterface.GetCntpctEl0", new NativeInterfaceGetCntpctEl0Delegate(NativeInterface.GetCntpctEl0) },
|
||||||
|
{ "NativeInterface.GetCntvctEl0", new NativeInterfaceGetCntvctEl0Delegate(NativeInterface.GetCntvctEl0) },
|
||||||
|
{ "NativeInterface.GetCtrEl0", new NativeInterfaceGetCtrEl0Delegate(NativeInterface.GetCtrEl0) },
|
||||||
|
{ "NativeInterface.GetDczidEl0", new NativeInterfaceGetDczidEl0Delegate(NativeInterface.GetDczidEl0) },
|
||||||
|
{ "NativeInterface.GetFunctionAddress", new NativeInterfaceGetFunctionAddressDelegate(NativeInterface.GetFunctionAddress) },
|
||||||
|
{ "NativeInterface.InvalidateCacheLine", new NativeInterfaceInvalidateCacheLineDelegate(NativeInterface.InvalidateCacheLine) },
|
||||||
|
{ "NativeInterface.ReadByte", new NativeInterfaceReadByteDelegate(NativeInterface.ReadByte) },
|
||||||
|
{ "NativeInterface.ReadUInt16", new NativeInterfaceReadUInt16Delegate(NativeInterface.ReadUInt16) },
|
||||||
|
{ "NativeInterface.ReadUInt32", new NativeInterfaceReadUInt32Delegate(NativeInterface.ReadUInt32) },
|
||||||
|
{ "NativeInterface.ReadUInt64", new NativeInterfaceReadUInt64Delegate(NativeInterface.ReadUInt64) },
|
||||||
|
{ "NativeInterface.ReadVector128", new NativeInterfaceReadVector128Delegate(NativeInterface.ReadVector128) },
|
||||||
|
{ "NativeInterface.SignalMemoryTracking", new NativeInterfaceSignalMemoryTrackingDelegate(NativeInterface.SignalMemoryTracking) },
|
||||||
|
{ "NativeInterface.SupervisorCall", new NativeInterfaceSupervisorCallDelegate(NativeInterface.SupervisorCall) },
|
||||||
|
{ "NativeInterface.ThrowInvalidMemoryAccess", new NativeInterfaceThrowInvalidMemoryAccessDelegate(NativeInterface.ThrowInvalidMemoryAccess) },
|
||||||
|
{ "NativeInterface.Undefined", new NativeInterfaceUndefinedDelegate(NativeInterface.Undefined) },
|
||||||
|
{ "NativeInterface.WriteByte", new NativeInterfaceWriteByteDelegate(NativeInterface.WriteByte) },
|
||||||
|
{ "NativeInterface.WriteUInt16", new NativeInterfaceWriteUInt16Delegate(NativeInterface.WriteUInt16) },
|
||||||
|
{ "NativeInterface.WriteUInt32", new NativeInterfaceWriteUInt32Delegate(NativeInterface.WriteUInt32) },
|
||||||
|
{ "NativeInterface.WriteUInt64", new NativeInterfaceWriteUInt64Delegate(NativeInterface.WriteUInt64) },
|
||||||
|
{ "NativeInterface.WriteVector128", new NativeInterfaceWriteVector128Delegate(NativeInterface.WriteVector128) },
|
||||||
|
|
||||||
|
// SoftFallback delegates
|
||||||
|
{ "SoftFallback.CountLeadingSigns", new SoftFallbackCountLeadingSignsDelegate(SoftFallback.CountLeadingSigns) },
|
||||||
|
{ "SoftFallback.CountLeadingZeros", new SoftFallbackCountLeadingZerosDelegate(SoftFallback.CountLeadingZeros) },
|
||||||
|
{ "SoftFallback.Crc32b", new SoftFallbackCrc32bDelegate(SoftFallback.Crc32b) },
|
||||||
|
{ "SoftFallback.Crc32cb", new SoftFallbackCrc32cbDelegate(SoftFallback.Crc32cb) },
|
||||||
|
{ "SoftFallback.Crc32ch", new SoftFallbackCrc32chDelegate(SoftFallback.Crc32ch) },
|
||||||
|
{ "SoftFallback.Crc32cw", new SoftFallbackCrc32cwDelegate(SoftFallback.Crc32cw) },
|
||||||
|
{ "SoftFallback.Crc32cx", new SoftFallbackCrc32cxDelegate(SoftFallback.Crc32cx) },
|
||||||
|
{ "SoftFallback.Crc32h", new SoftFallbackCrc32hDelegate(SoftFallback.Crc32h) },
|
||||||
|
{ "SoftFallback.Crc32w", new SoftFallbackCrc32wDelegate(SoftFallback.Crc32w) },
|
||||||
|
{ "SoftFallback.Crc32x", new SoftFallbackCrc32xDelegate(SoftFallback.Crc32x) },
|
||||||
|
{ "SoftFallback.Decrypt", new SoftFallbackDecryptDelegate(SoftFallback.Decrypt) },
|
||||||
|
{ "SoftFallback.Encrypt", new SoftFallbackEncryptDelegate(SoftFallback.Encrypt) },
|
||||||
|
{ "SoftFallback.FixedRotate", new SoftFallbackFixedRotateDelegate(SoftFallback.FixedRotate) },
|
||||||
|
{ "SoftFallback.HashChoose", new SoftFallbackHashChooseDelegate(SoftFallback.HashChoose) },
|
||||||
|
{ "SoftFallback.HashLower", new SoftFallbackHashLowerDelegate(SoftFallback.HashLower) },
|
||||||
|
{ "SoftFallback.HashMajority", new SoftFallbackHashMajorityDelegate(SoftFallback.HashMajority) },
|
||||||
|
{ "SoftFallback.HashParity", new SoftFallbackHashParityDelegate(SoftFallback.HashParity) },
|
||||||
|
{ "SoftFallback.HashUpper", new SoftFallbackHashUpperDelegate(SoftFallback.HashUpper) },
|
||||||
|
{ "SoftFallback.InverseMixColumns", new SoftFallbackInverseMixColumnsDelegate(SoftFallback.InverseMixColumns) },
|
||||||
|
{ "SoftFallback.MixColumns", new SoftFallbackMixColumnsDelegate(SoftFallback.MixColumns) },
|
||||||
|
{ "SoftFallback.PolynomialMult64_128", new SoftFallbackPolynomialMult64_128Delegate(SoftFallback.PolynomialMult64_128) },
|
||||||
|
{ "SoftFallback.SatF32ToS32", new SoftFallbackSatF32ToS32Delegate(SoftFallback.SatF32ToS32) },
|
||||||
|
{ "SoftFallback.SatF32ToS64", new SoftFallbackSatF32ToS64Delegate(SoftFallback.SatF32ToS64) },
|
||||||
|
{ "SoftFallback.SatF32ToU32", new SoftFallbackSatF32ToU32Delegate(SoftFallback.SatF32ToU32) },
|
||||||
|
{ "SoftFallback.SatF32ToU64", new SoftFallbackSatF32ToU64Delegate(SoftFallback.SatF32ToU64) },
|
||||||
|
{ "SoftFallback.SatF64ToS32", new SoftFallbackSatF64ToS32Delegate(SoftFallback.SatF64ToS32) },
|
||||||
|
{ "SoftFallback.SatF64ToS64", new SoftFallbackSatF64ToS64Delegate(SoftFallback.SatF64ToS64) },
|
||||||
|
{ "SoftFallback.SatF64ToU32", new SoftFallbackSatF64ToU32Delegate(SoftFallback.SatF64ToU32) },
|
||||||
|
{ "SoftFallback.SatF64ToU64", new SoftFallbackSatF64ToU64Delegate(SoftFallback.SatF64ToU64) },
|
||||||
|
{ "SoftFallback.Sha1SchedulePart1", new SoftFallbackSha1SchedulePart1Delegate(SoftFallback.Sha1SchedulePart1) },
|
||||||
|
{ "SoftFallback.Sha1SchedulePart2", new SoftFallbackSha1SchedulePart2Delegate(SoftFallback.Sha1SchedulePart2) },
|
||||||
|
{ "SoftFallback.Sha256SchedulePart1", new SoftFallbackSha256SchedulePart1Delegate(SoftFallback.Sha256SchedulePart1) },
|
||||||
|
{ "SoftFallback.Sha256SchedulePart2", new SoftFallbackSha256SchedulePart2Delegate(SoftFallback.Sha256SchedulePart2) },
|
||||||
|
{ "SoftFallback.SignedShrImm64", new SoftFallbackSignedShrImm64Delegate(SoftFallback.SignedShrImm64) },
|
||||||
|
{ "SoftFallback.Tbl1", new SoftFallbackTbl1Delegate(SoftFallback.Tbl1) },
|
||||||
|
{ "SoftFallback.Tbl2", new SoftFallbackTbl2Delegate(SoftFallback.Tbl2) },
|
||||||
|
{ "SoftFallback.Tbl3", new SoftFallbackTbl3Delegate(SoftFallback.Tbl3) },
|
||||||
|
{ "SoftFallback.Tbl4", new SoftFallbackTbl4Delegate(SoftFallback.Tbl4) },
|
||||||
|
{ "SoftFallback.Tbx1", new SoftFallbackTbx1Delegate(SoftFallback.Tbx1) },
|
||||||
|
{ "SoftFallback.Tbx2", new SoftFallbackTbx2Delegate(SoftFallback.Tbx2) },
|
||||||
|
{ "SoftFallback.Tbx3", new SoftFallbackTbx3Delegate(SoftFallback.Tbx3) },
|
||||||
|
{ "SoftFallback.Tbx4", new SoftFallbackTbx4Delegate(SoftFallback.Tbx4) },
|
||||||
|
{ "SoftFallback.UnsignedShrImm64", new SoftFallbackUnsignedShrImm64Delegate(SoftFallback.UnsignedShrImm64) },
|
||||||
|
|
||||||
|
// SoftFloat delegates
|
||||||
|
{ "SoftFloat16_32.FPConvert", new SoftFloat16_32FPConvertDelegate(SoftFloat16_32.FPConvert) },
|
||||||
|
{ "SoftFloat16_64.FPConvert", new SoftFloat16_64FPConvertDelegate(SoftFloat16_64.FPConvert) },
|
||||||
|
|
||||||
|
{ "SoftFloat32.FPAdd", new SoftFloat32FPAddDelegate(SoftFloat32.FPAdd) },
|
||||||
|
{ "SoftFloat32.FPAddFpscr", new SoftFloat32FPAddFpscrDelegate(SoftFloat32.FPAddFpscr) },
|
||||||
|
{ "SoftFloat32.FPCompare", new SoftFloat32FPCompareDelegate(SoftFloat32.FPCompare) },
|
||||||
|
{ "SoftFloat32.FPCompareEQ", new SoftFloat32FPCompareEQDelegate(SoftFloat32.FPCompareEQ) },
|
||||||
|
{ "SoftFloat32.FPCompareEQFpscr", new SoftFloat32FPCompareEQFpscrDelegate(SoftFloat32.FPCompareEQFpscr) },
|
||||||
|
{ "SoftFloat32.FPCompareGE", new SoftFloat32FPCompareGEDelegate(SoftFloat32.FPCompareGE) },
|
||||||
|
{ "SoftFloat32.FPCompareGEFpscr", new SoftFloat32FPCompareGEFpscrDelegate(SoftFloat32.FPCompareGEFpscr) },
|
||||||
|
{ "SoftFloat32.FPCompareGT", new SoftFloat32FPCompareGTDelegate(SoftFloat32.FPCompareGT) },
|
||||||
|
{ "SoftFloat32.FPCompareGTFpscr", new SoftFloat32FPCompareGTFpscrDelegate(SoftFloat32.FPCompareGTFpscr) },
|
||||||
|
{ "SoftFloat32.FPCompareLE", new SoftFloat32FPCompareLEDelegate(SoftFloat32.FPCompareLE) },
|
||||||
|
{ "SoftFloat32.FPCompareLEFpscr", new SoftFloat32FPCompareLEFpscrDelegate(SoftFloat32.FPCompareLEFpscr) },
|
||||||
|
{ "SoftFloat32.FPCompareLT", new SoftFloat32FPCompareLTDelegate(SoftFloat32.FPCompareLT) },
|
||||||
|
{ "SoftFloat32.FPCompareLTFpscr", new SoftFloat32FPCompareLTFpscrDelegate(SoftFloat32.FPCompareLTFpscr) },
|
||||||
|
{ "SoftFloat32.FPDiv", new SoftFloat32FPDivDelegate(SoftFloat32.FPDiv) },
|
||||||
|
{ "SoftFloat32.FPMax", new SoftFloat32FPMaxDelegate(SoftFloat32.FPMax) },
|
||||||
|
{ "SoftFloat32.FPMaxFpscr", new SoftFloat32FPMaxFpscrDelegate(SoftFloat32.FPMaxFpscr) },
|
||||||
|
{ "SoftFloat32.FPMaxNum", new SoftFloat32FPMaxNumDelegate(SoftFloat32.FPMaxNum) },
|
||||||
|
{ "SoftFloat32.FPMaxNumFpscr", new SoftFloat32FPMaxNumFpscrDelegate(SoftFloat32.FPMaxNumFpscr) },
|
||||||
|
{ "SoftFloat32.FPMin", new SoftFloat32FPMinDelegate(SoftFloat32.FPMin) },
|
||||||
|
{ "SoftFloat32.FPMinFpscr", new SoftFloat32FPMinFpscrDelegate(SoftFloat32.FPMinFpscr) },
|
||||||
|
{ "SoftFloat32.FPMinNum", new SoftFloat32FPMinNumDelegate(SoftFloat32.FPMinNum) },
|
||||||
|
{ "SoftFloat32.FPMinNumFpscr", new SoftFloat32FPMinNumFpscrDelegate(SoftFloat32.FPMinNumFpscr) },
|
||||||
|
{ "SoftFloat32.FPMul", new SoftFloat32FPMulDelegate(SoftFloat32.FPMul) },
|
||||||
|
{ "SoftFloat32.FPMulFpscr", new SoftFloat32FPMulFpscrDelegate(SoftFloat32.FPMulFpscr) },
|
||||||
|
{ "SoftFloat32.FPMulAdd", new SoftFloat32FPMulAddDelegate(SoftFloat32.FPMulAdd) },
|
||||||
|
{ "SoftFloat32.FPMulAddFpscr", new SoftFloat32FPMulAddFpscrDelegate(SoftFloat32.FPMulAddFpscr) },
|
||||||
|
{ "SoftFloat32.FPMulSub", new SoftFloat32FPMulSubDelegate(SoftFloat32.FPMulSub) },
|
||||||
|
{ "SoftFloat32.FPMulSubFpscr", new SoftFloat32FPMulSubFpscrDelegate(SoftFloat32.FPMulSubFpscr) },
|
||||||
|
{ "SoftFloat32.FPMulX", new SoftFloat32FPMulXDelegate(SoftFloat32.FPMulX) },
|
||||||
|
{ "SoftFloat32.FPNegMulAdd", new SoftFloat32FPNegMulAddDelegate(SoftFloat32.FPNegMulAdd) },
|
||||||
|
{ "SoftFloat32.FPNegMulSub", new SoftFloat32FPNegMulSubDelegate(SoftFloat32.FPNegMulSub) },
|
||||||
|
{ "SoftFloat32.FPRecipEstimate", new SoftFloat32FPRecipEstimateDelegate(SoftFloat32.FPRecipEstimate) },
|
||||||
|
{ "SoftFloat32.FPRecipEstimateFpscr", new SoftFloat32FPRecipEstimateFpscrDelegate(SoftFloat32.FPRecipEstimateFpscr) },
|
||||||
|
{ "SoftFloat32.FPRecipStep", new SoftFloat32FPRecipStepDelegate(SoftFloat32.FPRecipStep) },
|
||||||
|
{ "SoftFloat32.FPRecipStepFused", new SoftFloat32FPRecipStepFusedDelegate(SoftFloat32.FPRecipStepFused) },
|
||||||
|
{ "SoftFloat32.FPRecpX", new SoftFloat32FPRecpXDelegate(SoftFloat32.FPRecpX) },
|
||||||
|
{ "SoftFloat32.FPRSqrtEstimate", new SoftFloat32FPRSqrtEstimateDelegate(SoftFloat32.FPRSqrtEstimate) },
|
||||||
|
{ "SoftFloat32.FPRSqrtEstimateFpscr", new SoftFloat32FPRSqrtEstimateFpscrDelegate(SoftFloat32.FPRSqrtEstimateFpscr) },
|
||||||
|
{ "SoftFloat32.FPRSqrtStep", new SoftFloat32FPRSqrtStepDelegate(SoftFloat32.FPRSqrtStep) },
|
||||||
|
{ "SoftFloat32.FPRSqrtStepFused", new SoftFloat32FPRSqrtStepFusedDelegate(SoftFloat32.FPRSqrtStepFused) },
|
||||||
|
{ "SoftFloat32.FPSqrt", new SoftFloat32FPSqrtDelegate(SoftFloat32.FPSqrt) },
|
||||||
|
{ "SoftFloat32.FPSub", new SoftFloat32FPSubDelegate(SoftFloat32.FPSub) },
|
||||||
|
|
||||||
|
{ "SoftFloat32_16.FPConvert", new SoftFloat32_16FPConvertDelegate(SoftFloat32_16.FPConvert) },
|
||||||
|
|
||||||
|
{ "SoftFloat64.FPAdd", new SoftFloat64FPAddDelegate(SoftFloat64.FPAdd) },
|
||||||
|
{ "SoftFloat64.FPAddFpscr", new SoftFloat64FPAddFpscrDelegate(SoftFloat64.FPAddFpscr) },
|
||||||
|
{ "SoftFloat64.FPCompare", new SoftFloat64FPCompareDelegate(SoftFloat64.FPCompare) },
|
||||||
|
{ "SoftFloat64.FPCompareEQ", new SoftFloat64FPCompareEQDelegate(SoftFloat64.FPCompareEQ) },
|
||||||
|
{ "SoftFloat64.FPCompareEQFpscr", new SoftFloat64FPCompareEQFpscrDelegate(SoftFloat64.FPCompareEQFpscr) },
|
||||||
|
{ "SoftFloat64.FPCompareGE", new SoftFloat64FPCompareGEDelegate(SoftFloat64.FPCompareGE) },
|
||||||
|
{ "SoftFloat64.FPCompareGEFpscr", new SoftFloat64FPCompareGEFpscrDelegate(SoftFloat64.FPCompareGEFpscr) },
|
||||||
|
{ "SoftFloat64.FPCompareGT", new SoftFloat64FPCompareGTDelegate(SoftFloat64.FPCompareGT) },
|
||||||
|
{ "SoftFloat64.FPCompareGTFpscr", new SoftFloat64FPCompareGTFpscrDelegate(SoftFloat64.FPCompareGTFpscr) },
|
||||||
|
{ "SoftFloat64.FPCompareLE", new SoftFloat64FPCompareLEDelegate(SoftFloat64.FPCompareLE) },
|
||||||
|
{ "SoftFloat64.FPCompareLEFpscr", new SoftFloat64FPCompareLEFpscrDelegate(SoftFloat64.FPCompareLEFpscr) },
|
||||||
|
{ "SoftFloat64.FPCompareLT", new SoftFloat64FPCompareLTDelegate(SoftFloat64.FPCompareLT) },
|
||||||
|
{ "SoftFloat64.FPCompareLTFpscr", new SoftFloat64FPCompareLTFpscrDelegate(SoftFloat64.FPCompareLTFpscr) },
|
||||||
|
{ "SoftFloat64.FPDiv", new SoftFloat64FPDivDelegate(SoftFloat64.FPDiv) },
|
||||||
|
{ "SoftFloat64.FPMax", new SoftFloat64FPMaxDelegate(SoftFloat64.FPMax) },
|
||||||
|
{ "SoftFloat64.FPMaxFpscr", new SoftFloat64FPMaxFpscrDelegate(SoftFloat64.FPMaxFpscr) },
|
||||||
|
{ "SoftFloat64.FPMaxNum", new SoftFloat64FPMaxNumDelegate(SoftFloat64.FPMaxNum) },
|
||||||
|
{ "SoftFloat64.FPMaxNumFpscr", new SoftFloat64FPMaxNumFpscrDelegate(SoftFloat64.FPMaxNumFpscr) },
|
||||||
|
{ "SoftFloat64.FPMin", new SoftFloat64FPMinDelegate(SoftFloat64.FPMin) },
|
||||||
|
{ "SoftFloat64.FPMinFpscr", new SoftFloat64FPMinFpscrDelegate(SoftFloat64.FPMinFpscr) },
|
||||||
|
{ "SoftFloat64.FPMinNum", new SoftFloat64FPMinNumDelegate(SoftFloat64.FPMinNum) },
|
||||||
|
{ "SoftFloat64.FPMinNumFpscr", new SoftFloat64FPMinNumFpscrDelegate(SoftFloat64.FPMinNumFpscr) },
|
||||||
|
{ "SoftFloat64.FPMul", new SoftFloat64FPMulDelegate(SoftFloat64.FPMul) },
|
||||||
|
{ "SoftFloat64.FPMulFpscr", new SoftFloat64FPMulFpscrDelegate(SoftFloat64.FPMulFpscr) },
|
||||||
|
{ "SoftFloat64.FPMulAdd", new SoftFloat64FPMulAddDelegate(SoftFloat64.FPMulAdd) },
|
||||||
|
{ "SoftFloat64.FPMulAddFpscr", new SoftFloat64FPMulAddFpscrDelegate(SoftFloat64.FPMulAddFpscr) },
|
||||||
|
{ "SoftFloat64.FPMulSub", new SoftFloat64FPMulSubDelegate(SoftFloat64.FPMulSub) },
|
||||||
|
{ "SoftFloat64.FPMulSubFpscr", new SoftFloat64FPMulSubFpscrDelegate(SoftFloat64.FPMulSubFpscr) },
|
||||||
|
{ "SoftFloat64.FPMulX", new SoftFloat64FPMulXDelegate(SoftFloat64.FPMulX) },
|
||||||
|
{ "SoftFloat64.FPNegMulAdd", new SoftFloat64FPNegMulAddDelegate(SoftFloat64.FPNegMulAdd) },
|
||||||
|
{ "SoftFloat64.FPNegMulSub", new SoftFloat64FPNegMulSubDelegate(SoftFloat64.FPNegMulSub) },
|
||||||
|
{ "SoftFloat64.FPRecipEstimate", new SoftFloat64FPRecipEstimateDelegate(SoftFloat64.FPRecipEstimate) },
|
||||||
|
{ "SoftFloat64.FPRecipEstimateFpscr", new SoftFloat64FPRecipEstimateFpscrDelegate(SoftFloat64.FPRecipEstimateFpscr) },
|
||||||
|
{ "SoftFloat64.FPRecipStep", new SoftFloat64FPRecipStepDelegate(SoftFloat64.FPRecipStep) },
|
||||||
|
{ "SoftFloat64.FPRecipStepFused", new SoftFloat64FPRecipStepFusedDelegate(SoftFloat64.FPRecipStepFused) },
|
||||||
|
{ "SoftFloat64.FPRecpX", new SoftFloat64FPRecpXDelegate(SoftFloat64.FPRecpX) },
|
||||||
|
{ "SoftFloat64.FPRSqrtEstimate", new SoftFloat64FPRSqrtEstimateDelegate(SoftFloat64.FPRSqrtEstimate) },
|
||||||
|
{ "SoftFloat64.FPRSqrtEstimateFpscr", new SoftFloat64FPRSqrtEstimateFpscrDelegate(SoftFloat64.FPRSqrtEstimateFpscr) },
|
||||||
|
{ "SoftFloat64.FPRSqrtStep", new SoftFloat64FPRSqrtStepDelegate(SoftFloat64.FPRSqrtStep) },
|
||||||
|
{ "SoftFloat64.FPRSqrtStepFused", new SoftFloat64FPRSqrtStepFusedDelegate(SoftFloat64.FPRSqrtStepFused) },
|
||||||
|
{ "SoftFloat64.FPSqrt", new SoftFloat64FPSqrtDelegate(SoftFloat64.FPSqrt) },
|
||||||
|
{ "SoftFloat64.FPSub", new SoftFloat64FPSubDelegate(SoftFloat64.FPSub) },
|
||||||
|
|
||||||
|
{ "SoftFloat64_16.FPConvert", new SoftFloat64_16FPConvertDelegate(SoftFloat64_16.FPConvert) }
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
SetDelegateInfo(typeof(MathHelper).GetMethod(nameof(MathHelper.Abs)));
|
SetDelegateInfo(typeof(MathHelper).GetMethod(nameof(MathHelper.Abs)));
|
||||||
SetDelegateInfo(typeof(MathHelper).GetMethod(nameof(MathHelper.Ceiling)));
|
SetDelegateInfo(typeof(MathHelper).GetMethod(nameof(MathHelper.Ceiling)));
|
||||||
SetDelegateInfo(typeof(MathHelper).GetMethod(nameof(MathHelper.Floor)));
|
SetDelegateInfo(typeof(MathHelper).GetMethod(nameof(MathHelper.Floor)));
|
||||||
@@ -152,44 +708,44 @@ namespace ARMeilleure.Translation
|
|||||||
SetDelegateInfo(typeof(SoftFloat16_64).GetMethod(nameof(SoftFloat16_64.FPConvert)));
|
SetDelegateInfo(typeof(SoftFloat16_64).GetMethod(nameof(SoftFloat16_64.FPConvert)));
|
||||||
|
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPAdd)));
|
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPAdd)));
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPAddFpscr))); // A32 only.
|
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPAddFpscr)));
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompare)));
|
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompare)));
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareEQ)));
|
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareEQ)));
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareEQFpscr))); // A32 only.
|
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareEQFpscr)));
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareGE)));
|
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareGE)));
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareGEFpscr))); // A32 only.
|
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareGEFpscr)));
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareGT)));
|
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareGT)));
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareGTFpscr))); // A32 only.
|
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareGTFpscr)));
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareLE)));
|
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareLE)));
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareLEFpscr))); // A32 only.
|
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareLEFpscr)));
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareLT)));
|
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareLT)));
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareLTFpscr))); // A32 only.
|
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompareLTFpscr)));
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPDiv)));
|
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPDiv)));
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMax)));
|
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMax)));
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMaxFpscr))); // A32 only.
|
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMaxFpscr)));
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMaxNum)));
|
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMaxNum)));
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMaxNumFpscr))); // A32 only.
|
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMaxNumFpscr)));
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMin)));
|
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMin)));
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMinFpscr))); // A32 only.
|
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMinFpscr)));
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMinNum)));
|
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMinNum)));
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMinNumFpscr))); // A32 only.
|
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMinNumFpscr)));
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMul)));
|
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMul)));
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulFpscr))); // A32 only.
|
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulFpscr)));
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulAdd)));
|
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulAdd)));
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulAddFpscr))); // A32 only.
|
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulAddFpscr)));
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulSub)));
|
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulSub)));
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulSubFpscr))); // A32 only.
|
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulSubFpscr)));
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulX)));
|
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPMulX)));
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPNegMulAdd)));
|
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPNegMulAdd)));
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPNegMulSub)));
|
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPNegMulSub)));
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRecipEstimate)));
|
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRecipEstimate)));
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRecipEstimateFpscr))); // A32 only.
|
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRecipEstimateFpscr)));
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRecipStep))); // A32 only.
|
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRecipStep)));
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRecipStepFused)));
|
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRecipStepFused)));
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRecpX)));
|
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRecpX)));
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRSqrtEstimate)));
|
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRSqrtEstimate)));
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRSqrtEstimateFpscr))); // A32 only.
|
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRSqrtEstimateFpscr)));
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRSqrtStep))); // A32 only.
|
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRSqrtStep)));
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRSqrtStepFused)));
|
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPRSqrtStepFused)));
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPSqrt)));
|
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPSqrt)));
|
||||||
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPSub)));
|
SetDelegateInfo(typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPSub)));
|
||||||
@@ -197,44 +753,44 @@ namespace ARMeilleure.Translation
|
|||||||
SetDelegateInfo(typeof(SoftFloat32_16).GetMethod(nameof(SoftFloat32_16.FPConvert)));
|
SetDelegateInfo(typeof(SoftFloat32_16).GetMethod(nameof(SoftFloat32_16.FPConvert)));
|
||||||
|
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPAdd)));
|
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPAdd)));
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPAddFpscr))); // A32 only.
|
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPAddFpscr)));
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompare)));
|
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompare)));
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareEQ)));
|
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareEQ)));
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareEQFpscr))); // A32 only.
|
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareEQFpscr)));
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareGE)));
|
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareGE)));
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareGEFpscr))); // A32 only.
|
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareGEFpscr)));
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareGT)));
|
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareGT)));
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareGTFpscr))); // A32 only.
|
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareGTFpscr)));
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareLE)));
|
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareLE)));
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareLEFpscr))); // A32 only.
|
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareLEFpscr)));
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareLT)));
|
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareLT)));
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareLTFpscr))); // A32 only.
|
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompareLTFpscr)));
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPDiv)));
|
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPDiv)));
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMax)));
|
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMax)));
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMaxFpscr))); // A32 only.
|
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMaxFpscr)));
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMaxNum)));
|
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMaxNum)));
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMaxNumFpscr))); // A32 only.
|
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMaxNumFpscr)));
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMin)));
|
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMin)));
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMinFpscr))); // A32 only.
|
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMinFpscr)));
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMinNum)));
|
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMinNum)));
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMinNumFpscr))); // A32 only.
|
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMinNumFpscr)));
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMul)));
|
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMul)));
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulFpscr))); // A32 only.
|
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulFpscr)));
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulAdd)));
|
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulAdd)));
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulAddFpscr))); // A32 only.
|
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulAddFpscr)));
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulSub)));
|
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulSub)));
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulSubFpscr))); // A32 only.
|
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulSubFpscr)));
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulX)));
|
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPMulX)));
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPNegMulAdd)));
|
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPNegMulAdd)));
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPNegMulSub)));
|
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPNegMulSub)));
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRecipEstimate)));
|
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRecipEstimate)));
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRecipEstimateFpscr))); // A32 only.
|
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRecipEstimateFpscr)));
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRecipStep))); // A32 only.
|
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRecipStep)));
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRecipStepFused)));
|
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRecipStepFused)));
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRecpX)));
|
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRecpX)));
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRSqrtEstimate)));
|
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRSqrtEstimate)));
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRSqrtEstimateFpscr))); // A32 only.
|
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRSqrtEstimateFpscr)));
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRSqrtStep))); // A32 only.
|
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRSqrtStep)));
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRSqrtStepFused)));
|
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPRSqrtStepFused)));
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPSqrt)));
|
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPSqrt)));
|
||||||
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPSub)));
|
SetDelegateInfo(typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPSub)));
|
||||||
|
|||||||
@@ -97,7 +97,14 @@ namespace ARMeilleure.Translation
|
|||||||
|
|
||||||
public virtual Operand Call(MethodInfo info, params Operand[] callArgs)
|
public virtual Operand Call(MethodInfo info, params Operand[] callArgs)
|
||||||
{
|
{
|
||||||
|
#if ANDROID
|
||||||
|
// For Android, use the Delegates class to get the function pointer
|
||||||
|
int index = Delegates.GetDelegateIndex(info);
|
||||||
|
nint funcPtr = Delegates.GetDelegateFuncPtrByIndex(index);
|
||||||
|
#else
|
||||||
|
// For other platforms, use direct method handle approach
|
||||||
nint funcPtr = info.MethodHandle.GetFunctionPointer();
|
nint funcPtr = info.MethodHandle.GetFunctionPointer();
|
||||||
|
#endif
|
||||||
|
|
||||||
OperandType returnType = GetOperandType(info.ReturnType);
|
OperandType returnType = GetOperandType(info.ReturnType);
|
||||||
|
|
||||||
|
|||||||
@@ -359,7 +359,10 @@ namespace ARMeilleure.Translation
|
|||||||
|
|
||||||
IntervalTreeNode<TK, TV> tmp = LeftOf(replacementNode) ?? RightOf(replacementNode);
|
IntervalTreeNode<TK, TV> tmp = LeftOf(replacementNode) ?? RightOf(replacementNode);
|
||||||
|
|
||||||
tmp?.Parent = ParentOf(replacementNode);
|
if (tmp != null)
|
||||||
|
{
|
||||||
|
tmp.Parent = ParentOf(replacementNode);
|
||||||
|
}
|
||||||
|
|
||||||
if (ParentOf(replacementNode) == null)
|
if (ParentOf(replacementNode) == null)
|
||||||
{
|
{
|
||||||
@@ -567,7 +570,10 @@ namespace ARMeilleure.Translation
|
|||||||
{
|
{
|
||||||
IntervalTreeNode<TK, TV> right = RightOf(node);
|
IntervalTreeNode<TK, TV> right = RightOf(node);
|
||||||
node.Right = LeftOf(right);
|
node.Right = LeftOf(right);
|
||||||
node.Right?.Parent = node;
|
if (node.Right != null)
|
||||||
|
{
|
||||||
|
node.Right.Parent = node;
|
||||||
|
}
|
||||||
IntervalTreeNode<TK, TV> nodeParent = ParentOf(node);
|
IntervalTreeNode<TK, TV> nodeParent = ParentOf(node);
|
||||||
right.Parent = nodeParent;
|
right.Parent = nodeParent;
|
||||||
if (nodeParent == null)
|
if (nodeParent == null)
|
||||||
@@ -595,7 +601,10 @@ namespace ARMeilleure.Translation
|
|||||||
{
|
{
|
||||||
IntervalTreeNode<TK, TV> left = LeftOf(node);
|
IntervalTreeNode<TK, TV> left = LeftOf(node);
|
||||||
node.Left = RightOf(left);
|
node.Left = RightOf(left);
|
||||||
node.Left?.Parent = node;
|
if (node.Left != null)
|
||||||
|
{
|
||||||
|
node.Left.Parent = node;
|
||||||
|
}
|
||||||
IntervalTreeNode<TK, TV> nodeParent = ParentOf(node);
|
IntervalTreeNode<TK, TV> nodeParent = ParentOf(node);
|
||||||
left.Parent = nodeParent;
|
left.Parent = nodeParent;
|
||||||
if (nodeParent == null)
|
if (nodeParent == null)
|
||||||
@@ -642,7 +651,10 @@ namespace ARMeilleure.Translation
|
|||||||
/// <param name="color">Color (Boolean)</param>
|
/// <param name="color">Color (Boolean)</param>
|
||||||
private static void SetColor(IntervalTreeNode<TK, TV> node, bool color)
|
private static void SetColor(IntervalTreeNode<TK, TV> node, bool color)
|
||||||
{
|
{
|
||||||
node?.Color = color;
|
if (node != null)
|
||||||
|
{
|
||||||
|
node.Color = color;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ using ARMeilleure.CodeGen.Unwinding;
|
|||||||
using ARMeilleure.Common;
|
using ARMeilleure.Common;
|
||||||
using ARMeilleure.Memory;
|
using ARMeilleure.Memory;
|
||||||
using ARMeilleure.State;
|
using ARMeilleure.State;
|
||||||
using ARMeilleure.Translation.Cache;
|
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Configuration;
|
using Ryujinx.Common.Configuration;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
@@ -32,14 +31,11 @@ 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 = 7020; //! To be incremented manually for each change to the ARMeilleure project.
|
private const uint InternalVersion = 7031; //! 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";
|
||||||
|
|
||||||
private const string TitleIdTextDefault = "0000000000000000";
|
|
||||||
private const string DisplayVersionDefault = "0";
|
|
||||||
|
|
||||||
public static readonly Symbol PageTableSymbol = new(SymbolType.Special, 1);
|
public static readonly Symbol PageTableSymbol = new(SymbolType.Special, 1);
|
||||||
public static readonly Symbol CountTableSymbol = new(SymbolType.Special, 2);
|
public static readonly Symbol CountTableSymbol = new(SymbolType.Special, 2);
|
||||||
public static readonly Symbol DispatchStubSymbol = new(SymbolType.Special, 3);
|
public static readonly Symbol DispatchStubSymbol = new(SymbolType.Special, 3);
|
||||||
@@ -50,8 +46,6 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
|
|
||||||
public PtcProfiler Profiler { get; }
|
public PtcProfiler Profiler { get; }
|
||||||
|
|
||||||
private readonly JitCache _jitCache;
|
|
||||||
|
|
||||||
// Carriers.
|
// Carriers.
|
||||||
private MemoryStream _infosStream;
|
private MemoryStream _infosStream;
|
||||||
private List<byte[]> _codesList;
|
private List<byte[]> _codesList;
|
||||||
@@ -67,8 +61,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
|
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
|
|
||||||
public string TitleIdText { get; private set; }
|
public PtcCacheInfo CacheInfo { get; private set; }
|
||||||
public string DisplayVersion { get; private set; }
|
|
||||||
|
|
||||||
private MemoryManagerType _memoryMode;
|
private MemoryManagerType _memoryMode;
|
||||||
|
|
||||||
@@ -82,7 +75,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
private volatile int _translateTotalCount;
|
private volatile int _translateTotalCount;
|
||||||
public event Action<PtcLoadingState, int, int> PtcStateChanged;
|
public event Action<PtcLoadingState, int, int> PtcStateChanged;
|
||||||
|
|
||||||
public Ptc(JitCache jitCache)
|
public Ptc()
|
||||||
{
|
{
|
||||||
Profiler = new PtcProfiler(this);
|
Profiler = new PtcProfiler(this);
|
||||||
|
|
||||||
@@ -93,12 +86,9 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
|
|
||||||
_waitEvent = new ManualResetEvent(true);
|
_waitEvent = new ManualResetEvent(true);
|
||||||
|
|
||||||
_jitCache = jitCache;
|
|
||||||
|
|
||||||
_disposed = false;
|
_disposed = false;
|
||||||
|
|
||||||
TitleIdText = TitleIdTextDefault;
|
CacheInfo = new PtcCacheInfo(0, null, null, 0, null, "Unknown", "default");
|
||||||
DisplayVersion = DisplayVersionDefault;
|
|
||||||
|
|
||||||
CachePathActual = string.Empty;
|
CachePathActual = string.Empty;
|
||||||
CachePathBackup = string.Empty;
|
CachePathBackup = string.Empty;
|
||||||
@@ -106,20 +96,24 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
Disable();
|
Disable();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Initialize(string titleIdText, string displayVersion, bool enabled, MemoryManagerType memoryMode, string cacheSelector)
|
public void Initialize(PtcCacheInfo cacheInfo, bool enabled, MemoryManagerType memoryMode)
|
||||||
{
|
{
|
||||||
Wait();
|
Wait();
|
||||||
|
|
||||||
Profiler.Wait();
|
Profiler.Wait();
|
||||||
Profiler.ClearEntries();
|
Profiler.ClearEntries();
|
||||||
|
|
||||||
Logger.Info?.Print(LogClass.Ptc, $"Initializing Profiled Persistent Translation Cache v{InternalVersion}\n\t\t (title: {titleIdText}, version: '{displayVersion}', selector: '{cacheSelector}', enabled: {enabled}).");
|
CacheInfo = cacheInfo;
|
||||||
|
|
||||||
if (!enabled || string.IsNullOrEmpty(titleIdText) || titleIdText == TitleIdTextDefault)
|
Logger.Info?.Print(
|
||||||
|
LogClass.Ptc,
|
||||||
|
$"Initializing Profiled Persistent Translation Cache v{InternalVersion}\n\t\t " +
|
||||||
|
$"(pid: {cacheInfo.ProcessId}, title: {cacheInfo.TitleIdText}, application: {cacheInfo.ApplicationIdText}, " +
|
||||||
|
$"programIndex: {cacheInfo.ProgramIndex}, version: '{cacheInfo.DisplayVersion}', kind: {cacheInfo.ProcessKind}, " +
|
||||||
|
$"selector: '{cacheInfo.CacheSelector}', key: '{cacheInfo.CacheKey}', enabled: {enabled}).");
|
||||||
|
|
||||||
|
if (!enabled || cacheInfo.TitleIdText == PtcCacheInfo.TitleIdTextDefault)
|
||||||
{
|
{
|
||||||
TitleIdText = TitleIdTextDefault;
|
|
||||||
DisplayVersion = DisplayVersionDefault;
|
|
||||||
|
|
||||||
CachePathActual = string.Empty;
|
CachePathActual = string.Empty;
|
||||||
CachePathBackup = string.Empty;
|
CachePathBackup = string.Empty;
|
||||||
|
|
||||||
@@ -128,12 +122,10 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
TitleIdText = titleIdText;
|
|
||||||
DisplayVersion = !string.IsNullOrEmpty(displayVersion) ? displayVersion : DisplayVersionDefault;
|
|
||||||
_memoryMode = memoryMode;
|
_memoryMode = memoryMode;
|
||||||
|
|
||||||
string workPathActual = Path.Combine(AppDataManager.GamesDirPath, TitleIdText, "cache", "cpu", ActualDir);
|
string workPathActual = Path.Combine(AppDataManager.GamesDirPath, CacheInfo.TitleIdText, "cache", "cpu", ActualDir);
|
||||||
string workPathBackup = Path.Combine(AppDataManager.GamesDirPath, TitleIdText, "cache", "cpu", BackupDir);
|
string workPathBackup = Path.Combine(AppDataManager.GamesDirPath, CacheInfo.TitleIdText, "cache", "cpu", BackupDir);
|
||||||
|
|
||||||
if (!Directory.Exists(workPathActual))
|
if (!Directory.Exists(workPathActual))
|
||||||
{
|
{
|
||||||
@@ -145,8 +137,14 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
Directory.CreateDirectory(workPathBackup);
|
Directory.CreateDirectory(workPathBackup);
|
||||||
}
|
}
|
||||||
|
|
||||||
CachePathActual = Path.Combine(workPathActual, DisplayVersion) + "-" + cacheSelector;
|
CachePathActual = Path.Combine(workPathActual, CacheInfo.DisplayVersion) + "-" + CacheInfo.CacheSelector;
|
||||||
CachePathBackup = Path.Combine(workPathBackup, DisplayVersion) + "-" + cacheSelector;
|
CachePathBackup = Path.Combine(workPathBackup, CacheInfo.DisplayVersion) + "-" + CacheInfo.CacheSelector;
|
||||||
|
|
||||||
|
Logger.Info?.Print(
|
||||||
|
LogClass.Ptc,
|
||||||
|
$"PPTC cache owner selected (pid: {CacheInfo.ProcessId}, title: {CacheInfo.TitleIdText}, application: {CacheInfo.ApplicationIdText}, " +
|
||||||
|
$"version: '{CacheInfo.DisplayVersion}', kind: {CacheInfo.ProcessKind}, selector: '{CacheInfo.CacheSelector}', " +
|
||||||
|
$"key: '{CacheInfo.CacheKey}', path: '{CachePathActual}').");
|
||||||
|
|
||||||
PreLoad();
|
PreLoad();
|
||||||
Profiler.PreLoad();
|
Profiler.PreLoad();
|
||||||
@@ -255,8 +253,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
outerHeader.FeatureInfo != GetFeatureInfo() ||
|
outerHeader.FeatureInfo != GetFeatureInfo() ||
|
||||||
outerHeader.MemoryManagerMode != GetMemoryManagerMode() ||
|
outerHeader.MemoryManagerMode != GetMemoryManagerMode() ||
|
||||||
outerHeader.OSPlatform != GetOSPlatform() ||
|
outerHeader.OSPlatform != GetOSPlatform() ||
|
||||||
outerHeader.Architecture != (uint)RuntimeInformation.ProcessArchitecture ||
|
outerHeader.Architecture != (uint)RuntimeInformation.ProcessArchitecture)
|
||||||
outerHeader.DebuggerMode != Optimizations.EnableDebugging)
|
|
||||||
{
|
{
|
||||||
InvalidateCompressedStream(compressedStream);
|
InvalidateCompressedStream(compressedStream);
|
||||||
|
|
||||||
@@ -370,7 +367,12 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
|
|
||||||
long fileSize = new FileInfo(fileName).Length;
|
long fileSize = new FileInfo(fileName).Length;
|
||||||
|
|
||||||
Logger.Info?.Print(LogClass.Ptc, $"{(isBackup ? "Loaded Backup Translation Cache" : "Loaded Translation Cache")} (size: {fileSize} bytes, translated functions: {GetEntriesCount()}).");
|
Logger.Info?.Print(
|
||||||
|
LogClass.Ptc,
|
||||||
|
$"{(isBackup ? "Loaded Backup Translation Cache" : "Loaded Translation Cache")} " +
|
||||||
|
$"(pid: {CacheInfo.ProcessId}, title: {CacheInfo.TitleIdText}, version: '{CacheInfo.DisplayVersion}', kind: {CacheInfo.ProcessKind}, " +
|
||||||
|
$"selector: '{CacheInfo.CacheSelector}', key: '{CacheInfo.CacheKey}', path: '{fileName}', " +
|
||||||
|
$"size: {fileSize} bytes, translated functions: {GetEntriesCount()}).");
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -432,7 +434,6 @@ 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>() +
|
||||||
@@ -730,7 +731,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
return new UnwindInfo(pushEntries, prologueSize);
|
return new UnwindInfo(pushEntries, prologueSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
private TranslatedFunction FastTranslate(
|
private static TranslatedFunction FastTranslate(
|
||||||
byte[] code,
|
byte[] code,
|
||||||
Counter<uint> callCounter,
|
Counter<uint> callCounter,
|
||||||
ulong guestSize,
|
ulong guestSize,
|
||||||
@@ -738,7 +739,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
bool highCq)
|
bool highCq)
|
||||||
{
|
{
|
||||||
CompiledFunction cFunc = new(code, unwindInfo, RelocInfo.Empty);
|
CompiledFunction cFunc = new(code, unwindInfo, RelocInfo.Empty);
|
||||||
GuestFunction gFunc = cFunc.MapWithPointer<GuestFunction>(_jitCache, out nint gFuncPointer);
|
GuestFunction gFunc = cFunc.MapWithPointer<GuestFunction>(out nint gFuncPointer);
|
||||||
|
|
||||||
return new TranslatedFunction(gFunc, gFuncPointer, callCounter, guestSize, highCq);
|
return new TranslatedFunction(gFunc, gFuncPointer, callCounter, guestSize, highCq);
|
||||||
}
|
}
|
||||||
@@ -1029,7 +1030,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
return osPlatform;
|
return osPlatform;
|
||||||
}
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 87*/)]
|
[StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 86*/)]
|
||||||
private struct OuterHeader
|
private struct OuterHeader
|
||||||
{
|
{
|
||||||
public ulong Magic;
|
public ulong Magic;
|
||||||
@@ -1041,7 +1042,6 @@ 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;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
namespace ARMeilleure.Translation.PTC
|
||||||
|
{
|
||||||
|
public readonly struct PtcCacheInfo
|
||||||
|
{
|
||||||
|
public const string TitleIdTextDefault = "0000000000000000";
|
||||||
|
public const string ApplicationIdTextDefault = "0000000000000000";
|
||||||
|
public const string DisplayVersionDefault = "0";
|
||||||
|
|
||||||
|
public ulong ProcessId { get; }
|
||||||
|
public string TitleIdText { get; }
|
||||||
|
public string ApplicationIdText { get; }
|
||||||
|
public byte ProgramIndex { get; }
|
||||||
|
public string DisplayVersion { get; }
|
||||||
|
public string ProcessKind { get; }
|
||||||
|
public string CacheSelector { get; }
|
||||||
|
|
||||||
|
public string CacheKey => $"{DisplayVersion}-{CacheSelector}";
|
||||||
|
|
||||||
|
public PtcCacheInfo(
|
||||||
|
ulong processId,
|
||||||
|
string titleIdText,
|
||||||
|
string applicationIdText,
|
||||||
|
byte programIndex,
|
||||||
|
string displayVersion,
|
||||||
|
string processKind,
|
||||||
|
string cacheSelector)
|
||||||
|
{
|
||||||
|
ProcessId = processId;
|
||||||
|
TitleIdText = !string.IsNullOrEmpty(titleIdText) ? titleIdText : TitleIdTextDefault;
|
||||||
|
ApplicationIdText = !string.IsNullOrEmpty(applicationIdText) ? applicationIdText : ApplicationIdTextDefault;
|
||||||
|
ProgramIndex = programIndex;
|
||||||
|
DisplayVersion = !string.IsNullOrEmpty(displayVersion) ? displayVersion : DisplayVersionDefault;
|
||||||
|
ProcessKind = processKind ?? string.Empty;
|
||||||
|
CacheSelector = string.IsNullOrEmpty(cacheSelector) ? "default" : cacheSelector;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,13 +9,13 @@ using System.Collections.Generic;
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.IO.Compression;
|
using System.IO.Compression;
|
||||||
using System.Linq;
|
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Timers;
|
using System.Timers;
|
||||||
using static ARMeilleure.Translation.PTC.PtcFormatter;
|
using static ARMeilleure.Translation.PTC.PtcFormatter;
|
||||||
using Timer = System.Timers.Timer;
|
using Timer = System.Timers.Timer;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace ARMeilleure.Translation.PTC
|
namespace ARMeilleure.Translation.PTC
|
||||||
{
|
{
|
||||||
@@ -23,7 +23,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
{
|
{
|
||||||
private const string OuterHeaderMagicString = "Pohd\0\0\0\0";
|
private const string OuterHeaderMagicString = "Pohd\0\0\0\0";
|
||||||
|
|
||||||
private const uint InternalVersion = 6698; //! Not to be incremented manually for each change to the ARMeilleure project.
|
private const uint InternalVersion = 7031; //! Not to be incremented manually for each change to the ARMeilleure project.
|
||||||
|
|
||||||
private static readonly uint[] _migrateInternalVersions =
|
private static readonly uint[] _migrateInternalVersions =
|
||||||
[
|
[
|
||||||
@@ -254,7 +254,12 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
|
|
||||||
long fileSize = new FileInfo(fileName).Length;
|
long fileSize = new FileInfo(fileName).Length;
|
||||||
|
|
||||||
Logger.Info?.Print(LogClass.Ptc, $"{(isBackup ? "Loaded Backup Profiling Info" : "Loaded Profiling Info")} (size: {fileSize} bytes, profiled functions: {ProfiledFuncs.Count}).");
|
Logger.Info?.Print(
|
||||||
|
LogClass.Ptc,
|
||||||
|
$"{(isBackup ? "Loaded Backup Profiling Info" : "Loaded Profiling Info")} " +
|
||||||
|
$"(pid: {_ptc.CacheInfo.ProcessId}, title: {_ptc.CacheInfo.TitleIdText}, version: '{_ptc.CacheInfo.DisplayVersion}', " +
|
||||||
|
$"kind: {_ptc.CacheInfo.ProcessKind}, selector: '{_ptc.CacheInfo.CacheSelector}', key: '{_ptc.CacheInfo.CacheKey}', " +
|
||||||
|
$"path: '{fileName}', size: {fileSize} bytes, profiled functions: {ProfiledFuncs.Count}).");
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -375,7 +380,11 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
|
|
||||||
if (fileSize != 0L)
|
if (fileSize != 0L)
|
||||||
{
|
{
|
||||||
Logger.Info?.Print(LogClass.Ptc, $"Saved Profiling Info (size: {fileSize} bytes, profiled functions: {profiledFuncsCount}).");
|
Logger.Info?.Print(
|
||||||
|
LogClass.Ptc,
|
||||||
|
$"Saved Profiling Info (pid: {_ptc.CacheInfo.ProcessId}, title: {_ptc.CacheInfo.TitleIdText}, version: '{_ptc.CacheInfo.DisplayVersion}', " +
|
||||||
|
$"kind: {_ptc.CacheInfo.ProcessKind}, selector: '{_ptc.CacheInfo.CacheSelector}', key: '{_ptc.CacheInfo.CacheKey}', " +
|
||||||
|
$"path: '{fileName}', size: {fileSize} bytes, profiled functions: {profiledFuncsCount}).");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,11 +24,10 @@ namespace ARMeilleure.Translation
|
|||||||
private readonly IJitMemoryAllocator _allocator;
|
private readonly IJitMemoryAllocator _allocator;
|
||||||
private readonly ConcurrentQueue<KeyValuePair<ulong, TranslatedFunction>> _oldFuncs;
|
private readonly ConcurrentQueue<KeyValuePair<ulong, TranslatedFunction>> _oldFuncs;
|
||||||
|
|
||||||
public readonly JitCache JitCache;
|
|
||||||
private readonly Ptc _ptc;
|
private readonly Ptc _ptc;
|
||||||
|
|
||||||
internal TranslatorCache<TranslatedFunction> Functions { get; }
|
internal TranslatorCache<TranslatedFunction> Functions { get; }
|
||||||
public IAddressTable<ulong> FunctionTable { get; }
|
internal IAddressTable<ulong> FunctionTable { get; }
|
||||||
internal EntryTable<uint> CountTable { get; }
|
internal EntryTable<uint> CountTable { get; }
|
||||||
internal TranslatorStubs Stubs { get; }
|
internal TranslatorStubs Stubs { get; }
|
||||||
internal TranslatorQueue Queue { get; }
|
internal TranslatorQueue Queue { get; }
|
||||||
@@ -44,26 +43,23 @@ namespace ARMeilleure.Translation
|
|||||||
|
|
||||||
_oldFuncs = new ConcurrentQueue<KeyValuePair<ulong, TranslatedFunction>>();
|
_oldFuncs = new ConcurrentQueue<KeyValuePair<ulong, TranslatedFunction>>();
|
||||||
|
|
||||||
JitCache = new JitCache(allocator);
|
_ptc = new Ptc();
|
||||||
|
|
||||||
_ptc = new Ptc(JitCache);
|
|
||||||
|
|
||||||
Queue = new TranslatorQueue();
|
Queue = new TranslatorQueue();
|
||||||
|
|
||||||
|
JitCache.Initialize(allocator);
|
||||||
|
|
||||||
CountTable = new EntryTable<uint>();
|
CountTable = new EntryTable<uint>();
|
||||||
Functions = new TranslatorCache<TranslatedFunction>();
|
Functions = new TranslatorCache<TranslatedFunction>();
|
||||||
|
|
||||||
FunctionTable = functionTable;
|
FunctionTable = functionTable;
|
||||||
|
Stubs = new TranslatorStubs(FunctionTable);
|
||||||
Stubs = new TranslatorStubs(JitCache, FunctionTable);
|
|
||||||
|
|
||||||
FunctionTable.Fill = (ulong)Stubs.SlowDispatchStub;
|
FunctionTable.Fill = (ulong)Stubs.SlowDispatchStub;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IPtcLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled, string cacheSelector)
|
public IPtcLoadState LoadDiskCache(PtcCacheInfo cacheInfo, bool enabled)
|
||||||
{
|
{
|
||||||
_ptc.Initialize(titleIdText, displayVersion, enabled, Memory.Type, cacheSelector);
|
_ptc.Initialize(cacheInfo, enabled, Memory.Type);
|
||||||
return _ptc;
|
return _ptc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,25 +119,7 @@ namespace ARMeilleure.Translation
|
|||||||
|
|
||||||
NativeInterface.RegisterThread(context, Memory, this);
|
NativeInterface.RegisterThread(context, Memory, this);
|
||||||
|
|
||||||
if (Optimizations.EnableDebugging)
|
if (Optimizations.UseUnmanagedDispatchLoop)
|
||||||
{
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
@@ -171,7 +149,6 @@ namespace ARMeilleure.Translation
|
|||||||
}
|
}
|
||||||
|
|
||||||
ClearJitCache();
|
ClearJitCache();
|
||||||
JitCache.Dispose();
|
|
||||||
|
|
||||||
Stubs.Dispose();
|
Stubs.Dispose();
|
||||||
FunctionTable.Dispose();
|
FunctionTable.Dispose();
|
||||||
@@ -199,7 +176,7 @@ namespace ARMeilleure.Translation
|
|||||||
return nextAddr;
|
return nextAddr;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ulong Step(State.ExecutionContext context, ulong address)
|
public 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);
|
||||||
|
|
||||||
@@ -210,8 +187,6 @@ 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))
|
||||||
@@ -255,8 +230,7 @@ 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);
|
||||||
|
|
||||||
@@ -310,7 +284,7 @@ namespace ARMeilleure.Translation
|
|||||||
_ptc.WriteCompiledFunction(address, funcSize, hash, highCq, compiledFunc);
|
_ptc.WriteCompiledFunction(address, funcSize, hash, highCq, compiledFunc);
|
||||||
}
|
}
|
||||||
|
|
||||||
GuestFunction func = compiledFunc.MapWithPointer<GuestFunction>(JitCache, out nint funcPointer);
|
GuestFunction func = compiledFunc.MapWithPointer<GuestFunction>(out nint funcPointer);
|
||||||
|
|
||||||
Allocators.ResetAll();
|
Allocators.ResetAll();
|
||||||
|
|
||||||
@@ -395,13 +369,9 @@ namespace ARMeilleure.Translation
|
|||||||
|
|
||||||
if (block.Exit)
|
if (block.Exit)
|
||||||
{
|
{
|
||||||
// Return to managed rather than tail call.
|
// Left option here as it may be useful if we need to return to managed rather than tail call in
|
||||||
bool useReturns = Optimizations.EnableDebugging;
|
// future. (eg. for debug)
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
@@ -425,11 +395,6 @@ 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)
|
||||||
@@ -526,14 +491,6 @@ 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 = [];
|
||||||
|
|||||||
@@ -14,8 +14,6 @@ namespace ARMeilleure.Translation
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
class TranslatorStubs : IDisposable
|
class TranslatorStubs : IDisposable
|
||||||
{
|
{
|
||||||
private readonly JitCache _jitCache;
|
|
||||||
|
|
||||||
private readonly Lazy<nint> _slowDispatchStub;
|
private readonly Lazy<nint> _slowDispatchStub;
|
||||||
|
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
@@ -85,15 +83,12 @@ namespace ARMeilleure.Translation
|
|||||||
/// Initializes a new instance of the <see cref="TranslatorStubs"/> class with the specified
|
/// Initializes a new instance of the <see cref="TranslatorStubs"/> class with the specified
|
||||||
/// <see cref="Translator"/> instance.
|
/// <see cref="Translator"/> instance.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="jitCache">Jit cache to map functions in</param>
|
|
||||||
/// <param name="functionTable">Function table used to store pointers to the functions that the guest code will call</param>
|
/// <param name="functionTable">Function table used to store pointers to the functions that the guest code will call</param>
|
||||||
/// <exception cref="ArgumentNullException"><paramref name="translator"/> is null</exception>
|
/// <exception cref="ArgumentNullException"><paramref name="translator"/> is null</exception>
|
||||||
public TranslatorStubs(JitCache jitCache, IAddressTable<ulong> functionTable)
|
public TranslatorStubs(IAddressTable<ulong> functionTable)
|
||||||
{
|
{
|
||||||
ArgumentNullException.ThrowIfNull(functionTable);
|
ArgumentNullException.ThrowIfNull(functionTable);
|
||||||
|
|
||||||
_jitCache = jitCache;
|
|
||||||
|
|
||||||
_functionTable = functionTable;
|
_functionTable = functionTable;
|
||||||
_slowDispatchStub = new(GenerateSlowDispatchStub, isThreadSafe: true);
|
_slowDispatchStub = new(GenerateSlowDispatchStub, isThreadSafe: true);
|
||||||
_dispatchStub = new(GenerateDispatchStub, isThreadSafe: true);
|
_dispatchStub = new(GenerateDispatchStub, isThreadSafe: true);
|
||||||
@@ -120,12 +115,12 @@ namespace ARMeilleure.Translation
|
|||||||
{
|
{
|
||||||
if (_dispatchStub.IsValueCreated)
|
if (_dispatchStub.IsValueCreated)
|
||||||
{
|
{
|
||||||
_jitCache.Unmap(_dispatchStub.Value);
|
JitCache.Unmap(_dispatchStub.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_dispatchLoop.IsValueCreated)
|
if (_dispatchLoop.IsValueCreated)
|
||||||
{
|
{
|
||||||
_jitCache.Unmap(Marshal.GetFunctionPointerForDelegate(_dispatchLoop.Value));
|
JitCache.Unmap(Marshal.GetFunctionPointerForDelegate(_dispatchLoop.Value));
|
||||||
}
|
}
|
||||||
|
|
||||||
_disposed = true;
|
_disposed = true;
|
||||||
@@ -193,7 +188,7 @@ namespace ARMeilleure.Translation
|
|||||||
OperandType retType = OperandType.I64;
|
OperandType retType = OperandType.I64;
|
||||||
OperandType[] argTypes = new[] { OperandType.I64 };
|
OperandType[] argTypes = new[] { OperandType.I64 };
|
||||||
|
|
||||||
GuestFunction func = Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<GuestFunction>(_jitCache);
|
GuestFunction func = Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<GuestFunction>();
|
||||||
|
|
||||||
return Marshal.GetFunctionPointerForDelegate(func);
|
return Marshal.GetFunctionPointerForDelegate(func);
|
||||||
}
|
}
|
||||||
@@ -218,7 +213,7 @@ namespace ARMeilleure.Translation
|
|||||||
OperandType retType = OperandType.I64;
|
OperandType retType = OperandType.I64;
|
||||||
OperandType[] argTypes = new[] { OperandType.I64 };
|
OperandType[] argTypes = new[] { OperandType.I64 };
|
||||||
|
|
||||||
GuestFunction func = Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<GuestFunction>(_jitCache);
|
GuestFunction func = Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<GuestFunction>();
|
||||||
|
|
||||||
return Marshal.GetFunctionPointerForDelegate(func);
|
return Marshal.GetFunctionPointerForDelegate(func);
|
||||||
}
|
}
|
||||||
@@ -295,7 +290,7 @@ namespace ARMeilleure.Translation
|
|||||||
OperandType retType = OperandType.None;
|
OperandType retType = OperandType.None;
|
||||||
OperandType[] argTypes = new[] { OperandType.I64, OperandType.I64 };
|
OperandType[] argTypes = new[] { OperandType.I64, OperandType.I64 };
|
||||||
|
|
||||||
return Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<DispatcherFunction>(_jitCache);
|
return Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<DispatcherFunction>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -319,7 +314,7 @@ namespace ARMeilleure.Translation
|
|||||||
OperandType retType = OperandType.I64;
|
OperandType retType = OperandType.I64;
|
||||||
OperandType[] argTypes = new[] { OperandType.I64, OperandType.I64 };
|
OperandType[] argTypes = new[] { OperandType.I64, OperandType.I64 };
|
||||||
|
|
||||||
return Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<WrapperFunction>(_jitCache);
|
return Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<WrapperFunction>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
using ARMeilleure.CodeGen.X86;
|
using ARMeilleure.CodeGen.X86;
|
||||||
using ARMeilleure.IntermediateRepresentation;
|
using ARMeilleure.IntermediateRepresentation;
|
||||||
using ARMeilleure.State;
|
using ARMeilleure.State;
|
||||||
using ARMeilleure.Translation.Cache;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||||
@@ -60,7 +59,7 @@ namespace ARMeilleure.Translation
|
|||||||
return context.VectorExtract(OperandType.I32, vec, 0);
|
return context.VectorExtract(OperandType.I32, vec, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static FpFlagsPInvokeTest GenerateFpFlagsPInvokeTest(JitCache jitCache)
|
public static FpFlagsPInvokeTest GenerateFpFlagsPInvokeTest()
|
||||||
{
|
{
|
||||||
EmitterContext context = new();
|
EmitterContext context = new();
|
||||||
|
|
||||||
@@ -142,7 +141,7 @@ namespace ARMeilleure.Translation
|
|||||||
|
|
||||||
OperandType[] argTypes = [OperandType.I64];
|
OperandType[] argTypes = [OperandType.I64];
|
||||||
|
|
||||||
return Compiler.Compile(cfg, argTypes, OperandType.I32, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<FpFlagsPInvokeTest>(jitCache);
|
return Compiler.Compile(cfg, argTypes, OperandType.I32, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<FpFlagsPInvokeTest>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
.idea/
|
||||||
|
*.iml
|
||||||
|
.gradle
|
||||||
|
local.properties
|
||||||
|
.DS_Store
|
||||||
|
build/
|
||||||
|
captures
|
||||||
|
.externalNativeBuild
|
||||||
|
.cxx/
|
||||||
|
|
||||||
|
app/src/main/jniLibs/arm64-v8a/**
|
||||||
|
!app/src/main/jniLibs/arm64-v8a/.gitkeep
|
||||||
@@ -0,0 +1,175 @@
|
|||||||
|
plugins {
|
||||||
|
id 'com.android.application'
|
||||||
|
id 'org.jetbrains.kotlin.plugin.compose'
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace = 'org.kenjinx.android'
|
||||||
|
compileSdk 37
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
applicationId "org.kenjinx.android"
|
||||||
|
minSdk 29
|
||||||
|
targetSdk 37
|
||||||
|
versionCode 20100
|
||||||
|
versionName '2.1.0-pr.2'
|
||||||
|
|
||||||
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
|
||||||
|
ndk {
|
||||||
|
//noinspection ChromeOsAbiSupport
|
||||||
|
abiFilters 'arm64-v8a'
|
||||||
|
}
|
||||||
|
|
||||||
|
externalNativeBuild {
|
||||||
|
cmake {
|
||||||
|
cppFlags "-std=c++20 -O3",
|
||||||
|
"-fno-math-errno -fno-trapping-math -fno-signed-zeros -ffinite-math-only"
|
||||||
|
arguments "-DANDROID_STL=c++_shared",
|
||||||
|
"-DCMAKE_BUILD_TYPE=Release",
|
||||||
|
"-DANDROID_ARM_NEON=TRUE",
|
||||||
|
"-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
// minifyEnabled false
|
||||||
|
// proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||||
|
signingConfig = signingConfigs.debug
|
||||||
|
debuggable false
|
||||||
|
jniDebuggable false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def deviceFlavors = [
|
||||||
|
armv8 : "-march=armv8-a",
|
||||||
|
armv82 : "-march=armv8.2-a",
|
||||||
|
armv87 : "-march=armv8.7-a",
|
||||||
|
sd8eg5 : "-march=armv8.7-a+sve+sve2",
|
||||||
|
]
|
||||||
|
|
||||||
|
flavorDimensions "appid", "device"
|
||||||
|
productFlavors {
|
||||||
|
mainline {
|
||||||
|
dimension "appid"
|
||||||
|
applicationId "org.kenjinx.android"
|
||||||
|
manifestPlaceholders = [applicationLabel: "@string/app_name"]
|
||||||
|
}
|
||||||
|
optimized {
|
||||||
|
dimension "appid"
|
||||||
|
applicationId "com.garena.game.codm"
|
||||||
|
manifestPlaceholders = [applicationLabel: "@string/app_name_optimized"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deviceFlavors.each { name, flags ->
|
||||||
|
productFlavors.create(name) {
|
||||||
|
dimension "device"
|
||||||
|
externalNativeBuild {
|
||||||
|
cmake {
|
||||||
|
cppFlags flags
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
androidComponents {
|
||||||
|
onVariants(selector().withBuildType("debug")) {
|
||||||
|
packaging.dex.useLegacyPackaging.set(false)
|
||||||
|
}
|
||||||
|
beforeVariants(selector().all()) { variant ->
|
||||||
|
def flavors = variant.productFlavors.collect { it.second }.toSet()
|
||||||
|
|
||||||
|
def invalidCombos = [
|
||||||
|
["optimized", "armv8"],
|
||||||
|
["optimized", "armv87"],
|
||||||
|
["optimized", "sd8eg5"]
|
||||||
|
]
|
||||||
|
|
||||||
|
if (invalidCombos.any { combo -> flavors.containsAll(combo) }) {
|
||||||
|
enable = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility JavaVersion.VERSION_21
|
||||||
|
targetCompatibility JavaVersion.VERSION_21
|
||||||
|
}
|
||||||
|
|
||||||
|
buildFeatures {
|
||||||
|
compose = true
|
||||||
|
prefab = true
|
||||||
|
buildConfig = true
|
||||||
|
}
|
||||||
|
|
||||||
|
packagingOptions {
|
||||||
|
jniLibs {
|
||||||
|
keepDebugSymbols += '**/libkenjinx.so'
|
||||||
|
useLegacyPackaging true
|
||||||
|
}
|
||||||
|
dex {
|
||||||
|
useLegacyPackaging true
|
||||||
|
}
|
||||||
|
resources {
|
||||||
|
excludes += '/META-INF/{AL2.0,LGPL2.1}'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
externalNativeBuild {
|
||||||
|
cmake {
|
||||||
|
path file('src/main/cpp/CMakeLists.txt')
|
||||||
|
version = '4.1.2'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
main {
|
||||||
|
jniLibs.srcDirs = ['src/main/extLibs', 'src/main/jniLibs']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.named("preBuild") {
|
||||||
|
dependsOn ':libkenjinx:assemble'
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
runtimeOnly project(":libkenjinx")
|
||||||
|
implementation 'androidx.activity:activity-compose:1.13.0'
|
||||||
|
implementation 'androidx.appcompat:appcompat:1.8.0'
|
||||||
|
implementation 'androidx.compose.material3:material3'
|
||||||
|
implementation 'androidx.compose.material:material-icons-extended:1.7.8'
|
||||||
|
implementation 'androidx.compose.ui:ui'
|
||||||
|
implementation 'androidx.compose.ui:ui-graphics'
|
||||||
|
implementation 'androidx.compose.ui:ui-tooling-preview'
|
||||||
|
implementation 'androidx.constraintlayout:constraintlayout:2.2.2'
|
||||||
|
implementation 'androidx.core:core-ktx:1.19.0'
|
||||||
|
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.11.0'
|
||||||
|
implementation 'androidx.navigation:navigation-compose:2.9.8'
|
||||||
|
implementation 'androidx.preference:preference-ktx:1.2.1'
|
||||||
|
implementation 'br.com.devsrsouza.compose.icons:css-gg:1.1.1'
|
||||||
|
implementation 'com.anggrayudi:storage:2.2.0'
|
||||||
|
implementation 'com.github.swordfish90:radialgamepad:2.0.0'
|
||||||
|
implementation 'com.google.android.material:material:1.14.0'
|
||||||
|
implementation 'com.google.code.gson:gson:2.14.0'
|
||||||
|
implementation 'com.halilibo.compose-richtext:richtext-commonmark:0.20.0'
|
||||||
|
implementation 'com.halilibo.compose-richtext:richtext-ui-material3:0.20.0'
|
||||||
|
implementation 'com.halilibo.compose-richtext:richtext-ui:0.20.0'
|
||||||
|
implementation 'io.coil-kt:coil-compose:2.7.0'
|
||||||
|
implementation 'net.java.dev.jna:jna:5.19.1@aar'
|
||||||
|
implementation 'net.lingala.zip4j:zip4j:2.11.6'
|
||||||
|
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.11.0'
|
||||||
|
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.11.0'
|
||||||
|
implementation platform('androidx.compose:compose-bom:2026.08.00')
|
||||||
|
implementation platform('org.jetbrains.kotlin:kotlin-bom:2.3.10')
|
||||||
|
androidTestImplementation 'androidx.compose.ui:ui-test-junit4'
|
||||||
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.7.0'
|
||||||
|
androidTestImplementation 'androidx.test.ext:junit:1.3.0'
|
||||||
|
androidTestImplementation platform('androidx.compose:compose-bom:2026.08.00')
|
||||||
|
testImplementation 'junit:junit:4.13.2'
|
||||||
|
debugImplementation 'androidx.compose.ui:ui-test-manifest'
|
||||||
|
debugImplementation 'androidx.compose.ui:ui-tooling'
|
||||||
|
}
|
||||||
+37
@@ -0,0 +1,37 @@
|
|||||||
|
-optimizationpasses 5
|
||||||
|
-optimizations !code/simplification/arithmetic
|
||||||
|
-optimizations !code/simplification/cast
|
||||||
|
-optimizations !field/*
|
||||||
|
-optimizations !class/merging/*
|
||||||
|
-optimizations !method/inlining/short
|
||||||
|
-optimizations !method/inlining/unique
|
||||||
|
-optimizations method/inlining/tailrecursion
|
||||||
|
-optimizations method/removal/parameter
|
||||||
|
-optimizations code/merging
|
||||||
|
-optimizations code/simplification/variable
|
||||||
|
-allowaccessmodification
|
||||||
|
-repackageclasses ''
|
||||||
|
-keepattributes Exceptions,InnerClasses,Signature,*Annotation*
|
||||||
|
-dontpreverify
|
||||||
|
-keep public class * extends android.app.Activity
|
||||||
|
-keep public class * extends android.app.Application
|
||||||
|
-keep public class * extends android.app.Service
|
||||||
|
-keep public class * extends android.content.BroadcastReceiver
|
||||||
|
-keep public class * extends android.content.ContentProvider
|
||||||
|
-keep public class * extends androidx.fragment.app.Fragment
|
||||||
|
-dontwarn java.awt.Component
|
||||||
|
-dontwarn java.awt.GraphicsEnvironment
|
||||||
|
-dontwarn java.awt.HeadlessException
|
||||||
|
-dontwarn java.awt.Window
|
||||||
|
-dontwarn javax.lang.model.element.Modifier
|
||||||
|
-assumenosideeffects class java.lang.Math {
|
||||||
|
public static double random();
|
||||||
|
public static double sin(...);
|
||||||
|
public static double cos(...);
|
||||||
|
public static double sqrt(...);
|
||||||
|
}
|
||||||
|
-assumenosideeffects public class ** {
|
||||||
|
public boolean is*();
|
||||||
|
public boolean get*();
|
||||||
|
public boolean has*();
|
||||||
|
}
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<!-- Android 11+ Package Visibility: What this app is allowed to see/access -->
|
||||||
|
<queries>
|
||||||
|
<!-- If other flavors/starters are involved, make them visible here -->
|
||||||
|
<package android:name="org.kenjinx.android"/>
|
||||||
|
|
||||||
|
<!-- General: Make LAUNCHER activities and ACTION_VIEW files resolvable -->
|
||||||
|
<intent>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
</intent>
|
||||||
|
</queries>
|
||||||
|
|
||||||
|
<uses-feature
|
||||||
|
android:name="android.hardware.audio.output"
|
||||||
|
android:required="true" />
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
|
||||||
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||||
|
<uses-permission
|
||||||
|
android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
|
||||||
|
tools:ignore="ScopedStorage" />
|
||||||
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
|
<!-- Notifications + special-use Foreground Service (Android 14+) -->
|
||||||
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||||
|
<!-- Required for startForeground() -->
|
||||||
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK"/>
|
||||||
|
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
||||||
|
|
||||||
|
<application
|
||||||
|
android:name=".KenjinxApplication"
|
||||||
|
android:allowBackup="true"
|
||||||
|
android:appCategory="game"
|
||||||
|
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||||
|
android:fullBackupContent="@xml/backup_rules"
|
||||||
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
android:isGame="true"
|
||||||
|
android:label="${applicationLabel}"
|
||||||
|
android:supportsRtl="true"
|
||||||
|
android:theme="@style/Theme.KenjinxAndroid"
|
||||||
|
android:largeHeap="true"
|
||||||
|
android:requestLegacyExternalStorage="true"
|
||||||
|
tools:targetApi="31">
|
||||||
|
<activity
|
||||||
|
android:name=".MainActivity"
|
||||||
|
android:launchMode="singleTop"
|
||||||
|
android:exported="true"
|
||||||
|
android:screenOrientation="unspecified"
|
||||||
|
android:resizeableActivity="true"
|
||||||
|
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|uiMode"
|
||||||
|
android:hardwareAccelerated="true"
|
||||||
|
android:theme="@style/Theme.KenjinxAndroid">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="org.kenjinx.android.LAUNCH_GAME" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
<!--
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="org.kenji.android.action.CREATE_SHORTCUT" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
</intent-filter>
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!-- Shortcut Wizard Activity (transparent, freely rotatable) -->
|
||||||
|
<activity
|
||||||
|
android:name=".ShortcutWizardActivity"
|
||||||
|
android:exported="false"
|
||||||
|
android:theme="@style/Theme.KenjinxAndroid.Transparent"
|
||||||
|
android:screenOrientation="unspecified"
|
||||||
|
android:configChanges="orientation|screenSize|keyboardHidden|uiMode" />
|
||||||
|
|
||||||
|
<provider
|
||||||
|
android:name="androidx.core.content.FileProvider"
|
||||||
|
android:authorities="${applicationId}.fileprovider"
|
||||||
|
android:exported="false"
|
||||||
|
android:grantUriPermissions="true">
|
||||||
|
<meta-data
|
||||||
|
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||||
|
android:resource="@xml/provider_paths" />
|
||||||
|
</provider>
|
||||||
|
<provider
|
||||||
|
android:name=".providers.DocumentProvider"
|
||||||
|
android:authorities="${applicationId}.providers"
|
||||||
|
android:exported="true"
|
||||||
|
android:grantUriPermissions="true"
|
||||||
|
android:permission="android.permission.MANAGE_DOCUMENTS">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.content.action.DOCUMENTS_PROVIDER" />
|
||||||
|
</intent-filter>
|
||||||
|
</provider>
|
||||||
|
<service
|
||||||
|
android:name=".service.EmulationService"
|
||||||
|
android:exported="false"
|
||||||
|
android:stopWithTask="true"
|
||||||
|
android:foregroundServiceType="mediaPlayback" />
|
||||||
|
|
||||||
|
|
||||||
|
</application>
|
||||||
|
|
||||||
|
</manifest>
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
include(FetchContent)
|
||||||
|
|
||||||
|
# For more information about using CMake with Android Studio, read the
|
||||||
|
# documentation: https://d.android.com/studio/projects/add-native-code.html
|
||||||
|
|
||||||
|
# Sets the minimum version of CMake required to build the native library.
|
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 3.31.5)
|
||||||
|
|
||||||
|
# Declares and names the project.
|
||||||
|
|
||||||
|
project("kenjinxjni")
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
|
||||||
|
|
||||||
|
FetchContent_Declare(
|
||||||
|
adrenotools
|
||||||
|
GIT_REPOSITORY https://github.com/bylaws/libadrenotools.git
|
||||||
|
GIT_TAG 8fae8ce254dfc1344527e05301e43f37dea2df80
|
||||||
|
)
|
||||||
|
|
||||||
|
FetchContent_MakeAvailable(adrenotools)
|
||||||
|
|
||||||
|
# Creates and names a library, sets it as either STATIC
|
||||||
|
# or SHARED, and provides the relative paths to its source code.
|
||||||
|
# You can define multiple libraries, and CMake builds them for you.
|
||||||
|
# Gradle automatically packages shared libraries with your APK.
|
||||||
|
|
||||||
|
add_library( # Sets the name of the library.
|
||||||
|
kenjinxjni
|
||||||
|
|
||||||
|
# Sets the library as a shared library.
|
||||||
|
SHARED
|
||||||
|
|
||||||
|
# Provides a relative path to your source file(s).
|
||||||
|
vulkan_wrapper.cpp
|
||||||
|
kenjinx.cpp)
|
||||||
|
|
||||||
|
# Searches for a specified prebuilt library and stores the path as a
|
||||||
|
# variable. Because CMake includes system libraries in the search path by
|
||||||
|
# default, you only need to specify the name of the public NDK library
|
||||||
|
# you want to add. CMake verifies that the library exists before
|
||||||
|
# completing its build.
|
||||||
|
|
||||||
|
find_library( # Sets the name of the path variable.
|
||||||
|
log-lib
|
||||||
|
|
||||||
|
# Specifies the name of the NDK library that
|
||||||
|
# you want CMake to locate.
|
||||||
|
log )
|
||||||
|
|
||||||
|
# Specifies libraries CMake should link to your target library. You
|
||||||
|
# can link multiple libraries, such as libraries you define in this
|
||||||
|
# build script, prebuilt third-party libraries, or system libraries.
|
||||||
|
|
||||||
|
target_link_libraries( # Specifies the target library.
|
||||||
|
kenjinxjni
|
||||||
|
# Links the target library to the log library
|
||||||
|
# included in the NDK.
|
||||||
|
${log-lib}
|
||||||
|
-lvulkan
|
||||||
|
-landroid
|
||||||
|
adrenotools
|
||||||
|
)
|
||||||
|
|
||||||
|
# Build external libraries if prebuilt files don't exist
|
||||||
|
set(JNI_PATH ../jniLibs/${CMAKE_ANDROID_ARCH_ABI})
|
||||||
|
cmake_path(ABSOLUTE_PATH JNI_PATH NORMALIZE)
|
||||||
|
|
||||||
|
cmake_path(APPEND JNI_PATH libcrypto.so OUTPUT_VARIABLE LIBCRYPTO_JNI_PATH)
|
||||||
|
cmake_path(APPEND JNI_PATH libssl.so OUTPUT_VARIABLE LIBSSL_JNI_PATH)
|
||||||
|
|
||||||
|
# Add OpenAL
|
||||||
|
add_subdirectory(libraries/openal)
|
||||||
|
set_target_properties(OpenAL PROPERTIES
|
||||||
|
LIBRARY_OUTPUT_DIRECTORY ${JNI_PATH}
|
||||||
|
ARCHIVE_OUTPUT_DIRECTORY ${JNI_PATH}
|
||||||
|
RUNTIME_OUTPUT_DIRECTORY ${JNI_PATH}
|
||||||
|
)
|
||||||
@@ -0,0 +1,254 @@
|
|||||||
|
// Write C++ code here.
|
||||||
|
//
|
||||||
|
// Do not forget to dynamically load the C++ library into your application.
|
||||||
|
//
|
||||||
|
// For instance,
|
||||||
|
//
|
||||||
|
// In MainActivity.java:
|
||||||
|
// static {
|
||||||
|
// System.loadLibrary("kenjinxjni");
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Or, in MainActivity.kt:
|
||||||
|
// companion object {
|
||||||
|
// init {
|
||||||
|
// System.loadLibrary("kenjinxjni")
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
#include "kenjinx.h"
|
||||||
|
#include "pthread.h"
|
||||||
|
#include <chrono>
|
||||||
|
#include <csignal>
|
||||||
|
|
||||||
|
|
||||||
|
std::chrono::time_point<std::chrono::steady_clock, std::chrono::nanoseconds> _currentTimePoint;
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
JNIEXPORT jlong JNICALL
|
||||||
|
Java_org_kenjinx_android_NativeHelpers_getNativeWindow(
|
||||||
|
JNIEnv *env,
|
||||||
|
jobject instance,
|
||||||
|
jobject surface) {
|
||||||
|
auto nativeWindow = ANativeWindow_fromSurface(env, surface);
|
||||||
|
return nativeWindow == nullptr ? -1 : (jlong) nativeWindow;
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIEXPORT void JNICALL
|
||||||
|
Java_org_kenjinx_android_NativeHelpers_releaseNativeWindow(
|
||||||
|
JNIEnv *env,
|
||||||
|
jobject instance,
|
||||||
|
jlong window) {
|
||||||
|
auto nativeWindow = (ANativeWindow *) window;
|
||||||
|
|
||||||
|
if (nativeWindow != nullptr)
|
||||||
|
ANativeWindow_release(nativeWindow);
|
||||||
|
}
|
||||||
|
|
||||||
|
long createSurface(long native_surface, long instance) {
|
||||||
|
auto nativeWindow = (ANativeWindow *) native_surface;
|
||||||
|
VkSurfaceKHR surface;
|
||||||
|
auto vkInstance = (VkInstance) instance;
|
||||||
|
auto fpCreateAndroidSurfaceKHR =
|
||||||
|
reinterpret_cast<PFN_vkCreateAndroidSurfaceKHR>(vkGetInstanceProcAddr(vkInstance,
|
||||||
|
"vkCreateAndroidSurfaceKHR"));
|
||||||
|
if (fpCreateAndroidSurfaceKHR == nullptr)
|
||||||
|
LOGE("Could not get function pointer to CreateAndroidSurfaceKHR");
|
||||||
|
|
||||||
|
VkAndroidSurfaceCreateInfoKHR info;
|
||||||
|
info.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR;
|
||||||
|
info.pNext = nullptr;
|
||||||
|
info.flags = 0;
|
||||||
|
info.window = nativeWindow;
|
||||||
|
VK_CHECK(fpCreateAndroidSurfaceKHR(vkInstance, &info, nullptr, &surface));
|
||||||
|
return (long) surface;
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIEXPORT jlong JNICALL
|
||||||
|
Java_org_kenjinx_android_NativeHelpers_getCreateSurfacePtr(
|
||||||
|
JNIEnv *env,
|
||||||
|
jobject instance) {
|
||||||
|
return (jlong) createSurface;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *getStringPointer(
|
||||||
|
JNIEnv *env,
|
||||||
|
jstring jS) {
|
||||||
|
const char *cparam = env->GetStringUTFChars(jS, nullptr);
|
||||||
|
auto len = env->GetStringUTFLength(jS);
|
||||||
|
char *s = new char[len + 1]; //null terminator
|
||||||
|
strcpy(s, cparam);
|
||||||
|
env->ReleaseStringUTFChars(jS, cparam);
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
jstring createString(
|
||||||
|
JNIEnv *env,
|
||||||
|
char *ch) {
|
||||||
|
auto str = env->NewStringUTF(ch);
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
jstring createStringFromStdString(
|
||||||
|
JNIEnv *env,
|
||||||
|
std::string s) {
|
||||||
|
auto str = env->NewStringUTF(s.c_str());
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
extern "C"
|
||||||
|
void setRenderingThread() {
|
||||||
|
auto currentId = pthread_self();
|
||||||
|
|
||||||
|
_renderingThreadId = currentId;
|
||||||
|
|
||||||
|
_currentTimePoint = std::chrono::high_resolution_clock::now();
|
||||||
|
}
|
||||||
|
extern "C"
|
||||||
|
JNIEXPORT void JNICALL
|
||||||
|
Java_org_kenjinx_android_MainActivity_initVm(JNIEnv *env, jobject thiz) {
|
||||||
|
JavaVM *vm = nullptr;
|
||||||
|
env->GetJavaVM(&vm);
|
||||||
|
_vm = vm;
|
||||||
|
_mainActivity = thiz;
|
||||||
|
_mainActivityClass = env->GetObjectClass(thiz);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isInitialOrientationFlipped = true;
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
void setCurrentTransform(long native_window, int transform) {
|
||||||
|
if (native_window == 0 || native_window == -1)
|
||||||
|
return;
|
||||||
|
auto nativeWindow = (ANativeWindow *) native_window;
|
||||||
|
|
||||||
|
auto nativeTransform = ANativeWindowTransform::ANATIVEWINDOW_TRANSFORM_IDENTITY;
|
||||||
|
|
||||||
|
transform = transform >> 1;
|
||||||
|
|
||||||
|
// transform is a valid VkSurfaceTransformFlagBitsKHR
|
||||||
|
switch (transform) {
|
||||||
|
case 0x1:
|
||||||
|
nativeTransform = ANativeWindowTransform::ANATIVEWINDOW_TRANSFORM_IDENTITY;
|
||||||
|
break;
|
||||||
|
case 0x2:
|
||||||
|
nativeTransform = ANativeWindowTransform::ANATIVEWINDOW_TRANSFORM_ROTATE_90;
|
||||||
|
break;
|
||||||
|
case 0x4:
|
||||||
|
nativeTransform = isInitialOrientationFlipped
|
||||||
|
? ANativeWindowTransform::ANATIVEWINDOW_TRANSFORM_IDENTITY
|
||||||
|
: ANativeWindowTransform::ANATIVEWINDOW_TRANSFORM_ROTATE_180;
|
||||||
|
break;
|
||||||
|
case 0x8:
|
||||||
|
nativeTransform = ANativeWindowTransform::ANATIVEWINDOW_TRANSFORM_ROTATE_270;
|
||||||
|
break;
|
||||||
|
case 0x10:
|
||||||
|
nativeTransform = ANativeWindowTransform::ANATIVEWINDOW_TRANSFORM_MIRROR_HORIZONTAL;
|
||||||
|
break;
|
||||||
|
case 0x20:
|
||||||
|
nativeTransform = static_cast<ANativeWindowTransform>(
|
||||||
|
ANativeWindowTransform::ANATIVEWINDOW_TRANSFORM_MIRROR_HORIZONTAL |
|
||||||
|
ANATIVEWINDOW_TRANSFORM_ROTATE_90);
|
||||||
|
break;
|
||||||
|
case 0x40:
|
||||||
|
nativeTransform = ANativeWindowTransform::ANATIVEWINDOW_TRANSFORM_MIRROR_VERTICAL;
|
||||||
|
break;
|
||||||
|
case 0x80:
|
||||||
|
nativeTransform = static_cast<ANativeWindowTransform>(
|
||||||
|
ANativeWindowTransform::ANATIVEWINDOW_TRANSFORM_MIRROR_VERTICAL |
|
||||||
|
ANATIVEWINDOW_TRANSFORM_ROTATE_90);
|
||||||
|
break;
|
||||||
|
case 0x100:
|
||||||
|
nativeTransform = ANativeWindowTransform::ANATIVEWINDOW_TRANSFORM_IDENTITY;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
nativeWindow->perform(nativeWindow, NATIVE_WINDOW_SET_BUFFERS_TRANSFORM,
|
||||||
|
static_cast<int32_t>(nativeTransform));
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
JNIEXPORT jlong JNICALL
|
||||||
|
Java_org_kenjinx_android_NativeHelpers_loadDriver(JNIEnv *env, jobject thiz,
|
||||||
|
jstring native_lib_path,
|
||||||
|
jstring private_apps_path,
|
||||||
|
jstring driver_name) {
|
||||||
|
auto libPath = getStringPointer(env, native_lib_path);
|
||||||
|
auto privateAppsPath = getStringPointer(env, private_apps_path);
|
||||||
|
auto driverName = getStringPointer(env, driver_name);
|
||||||
|
|
||||||
|
auto handle = adrenotools_open_libvulkan(
|
||||||
|
RTLD_NOW,
|
||||||
|
ADRENOTOOLS_DRIVER_CUSTOM,
|
||||||
|
nullptr,
|
||||||
|
libPath,
|
||||||
|
privateAppsPath,
|
||||||
|
driverName,
|
||||||
|
nullptr,
|
||||||
|
nullptr
|
||||||
|
);
|
||||||
|
|
||||||
|
delete libPath;
|
||||||
|
delete privateAppsPath;
|
||||||
|
delete driverName;
|
||||||
|
|
||||||
|
return (jlong) handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
void debug_break(int code) {
|
||||||
|
if (code >= 3)
|
||||||
|
int r = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
JNIEXPORT void JNICALL
|
||||||
|
Java_org_kenjinx_android_NativeHelpers_setTurboMode(JNIEnv *env, jobject thiz, jboolean enable) {
|
||||||
|
adrenotools_set_turbo(enable);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
JNIEXPORT jint JNICALL
|
||||||
|
Java_org_kenjinx_android_NativeHelpers_getMaxSwapInterval(JNIEnv *env, jobject thiz,
|
||||||
|
jlong native_window) {
|
||||||
|
auto nativeWindow = (ANativeWindow *) native_window;
|
||||||
|
|
||||||
|
return nativeWindow->maxSwapInterval;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
JNIEXPORT jint JNICALL
|
||||||
|
Java_org_kenjinx_android_NativeHelpers_getMinSwapInterval(JNIEnv *env, jobject thiz,
|
||||||
|
jlong native_window) {
|
||||||
|
auto nativeWindow = (ANativeWindow *) native_window;
|
||||||
|
|
||||||
|
return nativeWindow->minSwapInterval;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
JNIEXPORT jint JNICALL
|
||||||
|
Java_org_kenjinx_android_NativeHelpers_setSwapInterval(JNIEnv *env, jobject thiz,
|
||||||
|
jlong native_window, jint swap_interval) {
|
||||||
|
auto nativeWindow = (ANativeWindow *) native_window;
|
||||||
|
|
||||||
|
return nativeWindow->setSwapInterval(nativeWindow, swap_interval);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
JNIEXPORT jstring JNICALL
|
||||||
|
Java_org_kenjinx_android_NativeHelpers_getStringJava(JNIEnv *env, jobject thiz, jlong ptr) {
|
||||||
|
return createString(env, (char*)ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
JNIEXPORT void JNICALL
|
||||||
|
Java_org_kenjinx_android_NativeHelpers_setIsInitialOrientationFlipped(JNIEnv *env, jobject thiz,
|
||||||
|
jboolean is_flipped) {
|
||||||
|
isInitialOrientationFlipped = is_flipped;
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
//
|
||||||
|
// Created by Emmanuel Hansen on 6/19/2023.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef KENJINXNATIVE_KENJINX_H
|
||||||
|
#define KENJINXNATIVE_KENJINX_H
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#include <exception>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <jni.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <android/log.h>
|
||||||
|
#include <android/native_window.h>
|
||||||
|
#include <android/native_window_jni.h>
|
||||||
|
|
||||||
|
#include "vulkan_wrapper.h"
|
||||||
|
#include <vulkan/vulkan_android.h>
|
||||||
|
#include "adrenotools/driver.h"
|
||||||
|
#include "native_window.h"
|
||||||
|
|
||||||
|
// Android log function wrappers
|
||||||
|
static const char* TAG = "Kenjinx";
|
||||||
|
#define LOGI(...) \
|
||||||
|
((void)__android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__))
|
||||||
|
#define LOGW(...) \
|
||||||
|
((void)__android_log_print(ANDROID_LOG_WARN, TAG, __VA_ARGS__))
|
||||||
|
#define LOGE(...) \
|
||||||
|
((void)__android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__))
|
||||||
|
|
||||||
|
// A macro to pass call to Vulkan and check for return value for success
|
||||||
|
#define CALL_VK(func) \
|
||||||
|
if (VK_SUCCESS != (func)) { \
|
||||||
|
__android_log_print(ANDROID_LOG_ERROR, "Tutorial ", \
|
||||||
|
"Vulkan error. File[%s], line[%d]", __FILE__, \
|
||||||
|
__LINE__); \
|
||||||
|
assert(false); \
|
||||||
|
}
|
||||||
|
|
||||||
|
// A macro to check value is VK_SUCCESS
|
||||||
|
// Used also for non-vulkan functions but return VK_SUCCESS
|
||||||
|
#define VK_CHECK(x) CALL_VK(x)
|
||||||
|
|
||||||
|
#define LoadLib(a) dlopen(a, RTLD_NOW)
|
||||||
|
|
||||||
|
void *_kenjinxNative = nullptr;
|
||||||
|
|
||||||
|
// Kenjinx imported functions
|
||||||
|
bool (*initialize)(char *) = nullptr;
|
||||||
|
|
||||||
|
long _renderingThreadId = 0;
|
||||||
|
JavaVM *_vm = nullptr;
|
||||||
|
jobject _mainActivity = nullptr;
|
||||||
|
jclass _mainActivityClass = nullptr;
|
||||||
|
|
||||||
|
#endif //KENJINXNATIVE_KENJINX_H
|
||||||
Submodule src/KenjinxAndroid/app/src/main/cpp/libraries/adrenotools added at 8fae8ce254
+1
Submodule src/KenjinxAndroid/app/src/main/cpp/libraries/openal added at c41d64c6a3
@@ -0,0 +1,305 @@
|
|||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||||
|
// Copyright © 2021 The Android Open Source Project
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
/* A collection of various types from AOSP that allow us to access private APIs for Native Window which we utilize for emulating the guest SF more accurately */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @url https://cs.android.com/android/platform/superproject/+/android11-release:frameworks/native/libs/nativebase/include/nativebase/nativebase.h;l=29;drc=cb496acbe593326e8d5d563847067d02b2df40ec
|
||||||
|
*/
|
||||||
|
#define ANDROID_NATIVE_UNSIGNED_CAST(x) static_cast<unsigned int>(x)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @url https://cs.android.com/android/platform/superproject/+/android11-release:frameworks/native/libs/nativebase/include/nativebase/nativebase.h;l=34-38;drc=cb496acbe593326e8d5d563847067d02b2df40ec
|
||||||
|
*/
|
||||||
|
#define ANDROID_NATIVE_MAKE_CONSTANT(a, b, c, d) \
|
||||||
|
((ANDROID_NATIVE_UNSIGNED_CAST(a) << 24) | \
|
||||||
|
(ANDROID_NATIVE_UNSIGNED_CAST(b) << 16) | \
|
||||||
|
(ANDROID_NATIVE_UNSIGNED_CAST(c) << 8) | \
|
||||||
|
(ANDROID_NATIVE_UNSIGNED_CAST(d)))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @url https://cs.android.com/android/platform/superproject/+/android11-release:frameworks/native/libs/nativewindow/include/system/window.h;l=60;drc=401cda638e7d17f6697b5a65c9a5ad79d056202d
|
||||||
|
*/
|
||||||
|
#define ANDROID_NATIVE_WINDOW_MAGIC ANDROID_NATIVE_MAKE_CONSTANT('_','w','n','d')
|
||||||
|
|
||||||
|
constexpr int AndroidNativeWindowMagic{ANDROID_NATIVE_WINDOW_MAGIC};
|
||||||
|
|
||||||
|
#undef ANDROID_NATIVE_WINDOW_MAGIC
|
||||||
|
#undef ANDROID_NATIVE_MAKE_CONSTANT
|
||||||
|
#undef ANDROID_NATIVE_UNSIGNED_CAST
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @url https://cs.android.com/android/platform/superproject/+/android11-release:frameworks/native/libs/nativewindow/include/system/window.h;l=325-331;drc=401cda638e7d17f6697b5a65c9a5ad79d056202d
|
||||||
|
*/
|
||||||
|
constexpr int64_t NativeWindowTimestampAuto{-9223372036854775807LL - 1};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @url https://cs.android.com/android/platform/superproject/+/android11-release:frameworks/native/libs/nativewindow/include/system/window.h;l=198-259;drc=401cda638e7d17f6697b5a65c9a5ad79d056202d
|
||||||
|
*/
|
||||||
|
enum {
|
||||||
|
NATIVE_WINDOW_CONNECT = 1, /* deprecated */
|
||||||
|
NATIVE_WINDOW_DISCONNECT = 2, /* deprecated */
|
||||||
|
NATIVE_WINDOW_SET_CROP = 3, /* private */
|
||||||
|
NATIVE_WINDOW_SET_BUFFER_COUNT = 4,
|
||||||
|
NATIVE_WINDOW_SET_BUFFERS_TRANSFORM = 6,
|
||||||
|
NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP = 7,
|
||||||
|
NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS = 8,
|
||||||
|
NATIVE_WINDOW_SET_SCALING_MODE = 10, /* private */
|
||||||
|
NATIVE_WINDOW_LOCK = 11, /* private */
|
||||||
|
NATIVE_WINDOW_UNLOCK_AND_POST = 12, /* private */
|
||||||
|
NATIVE_WINDOW_API_CONNECT = 13, /* private */
|
||||||
|
NATIVE_WINDOW_API_DISCONNECT = 14, /* private */
|
||||||
|
NATIVE_WINDOW_SET_BUFFERS_USER_DIMENSIONS = 15, /* private */
|
||||||
|
NATIVE_WINDOW_SET_POST_TRANSFORM_CROP = 16, /* deprecated, unimplemented */
|
||||||
|
NATIVE_WINDOW_SET_BUFFERS_STICKY_TRANSFORM = 17, /* private */
|
||||||
|
NATIVE_WINDOW_SET_SIDEBAND_STREAM = 18,
|
||||||
|
NATIVE_WINDOW_SET_BUFFERS_DATASPACE = 19,
|
||||||
|
NATIVE_WINDOW_SET_SURFACE_DAMAGE = 20, /* private */
|
||||||
|
NATIVE_WINDOW_SET_SHARED_BUFFER_MODE = 21,
|
||||||
|
NATIVE_WINDOW_SET_AUTO_REFRESH = 22,
|
||||||
|
NATIVE_WINDOW_GET_REFRESH_CYCLE_DURATION = 23,
|
||||||
|
NATIVE_WINDOW_GET_NEXT_FRAME_ID = 24,
|
||||||
|
NATIVE_WINDOW_ENABLE_FRAME_TIMESTAMPS = 25,
|
||||||
|
NATIVE_WINDOW_GET_COMPOSITOR_TIMING = 26,
|
||||||
|
NATIVE_WINDOW_GET_FRAME_TIMESTAMPS = 27,
|
||||||
|
NATIVE_WINDOW_GET_WIDE_COLOR_SUPPORT = 28,
|
||||||
|
NATIVE_WINDOW_GET_HDR_SUPPORT = 29,
|
||||||
|
NATIVE_WINDOW_GET_CONSUMER_USAGE64 = 31,
|
||||||
|
NATIVE_WINDOW_SET_BUFFERS_SMPTE2086_METADATA = 32,
|
||||||
|
NATIVE_WINDOW_SET_BUFFERS_CTA861_3_METADATA = 33,
|
||||||
|
NATIVE_WINDOW_SET_BUFFERS_HDR10_PLUS_METADATA = 34,
|
||||||
|
NATIVE_WINDOW_SET_AUTO_PREROTATION = 35,
|
||||||
|
NATIVE_WINDOW_GET_LAST_DEQUEUE_START = 36, /* private */
|
||||||
|
NATIVE_WINDOW_SET_DEQUEUE_TIMEOUT = 37, /* private */
|
||||||
|
NATIVE_WINDOW_GET_LAST_DEQUEUE_DURATION = 38, /* private */
|
||||||
|
NATIVE_WINDOW_GET_LAST_QUEUE_DURATION = 39, /* private */
|
||||||
|
NATIVE_WINDOW_SET_FRAME_RATE = 40,
|
||||||
|
NATIVE_WINDOW_SET_CANCEL_INTERCEPTOR = 41, /* private */
|
||||||
|
NATIVE_WINDOW_SET_DEQUEUE_INTERCEPTOR = 42, /* private */
|
||||||
|
NATIVE_WINDOW_SET_PERFORM_INTERCEPTOR = 43, /* private */
|
||||||
|
NATIVE_WINDOW_SET_QUEUE_INTERCEPTOR = 44, /* private */
|
||||||
|
NATIVE_WINDOW_ALLOCATE_BUFFERS = 45, /* private */
|
||||||
|
NATIVE_WINDOW_GET_LAST_QUEUED_BUFFER = 46, /* private */
|
||||||
|
NATIVE_WINDOW_SET_QUERY_INTERCEPTOR = 47, /* private */
|
||||||
|
NATIVE_WINDOW_GET_LAST_QUEUED_BUFFER2 = 50, /* private */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @url https://cs.android.com/android/platform/superproject/+/android11-release:frameworks/native/libs/nativebase/include/nativebase/nativebase.h;l=43-56;drc=cb496acbe593326e8d5d563847067d02b2df40ec
|
||||||
|
*/
|
||||||
|
struct android_native_base_t {
|
||||||
|
int magic;
|
||||||
|
int version;
|
||||||
|
void *reserved[4];
|
||||||
|
|
||||||
|
void (*incRef)(android_native_base_t *);
|
||||||
|
|
||||||
|
void (*decRef)(android_native_base_t *);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @url https://cs.android.com/android/platform/superproject/+/android11-release:frameworks/native/libs/nativewindow/include/system/window.h;l=341-560;drc=401cda638e7d17f6697b5a65c9a5ad79d056202d
|
||||||
|
*/
|
||||||
|
struct ANativeWindow {
|
||||||
|
struct android_native_base_t common;
|
||||||
|
|
||||||
|
/* flags describing some attributes of this surface or its updater */
|
||||||
|
const uint32_t flags;
|
||||||
|
|
||||||
|
/* min swap interval supported by this updated */
|
||||||
|
const int minSwapInterval;
|
||||||
|
|
||||||
|
/* max swap interval supported by this updated */
|
||||||
|
const int maxSwapInterval;
|
||||||
|
|
||||||
|
/* horizontal and vertical resolution in DPI */
|
||||||
|
const float xdpi;
|
||||||
|
const float ydpi;
|
||||||
|
|
||||||
|
/* Some storage reserved for the OEM's driver. */
|
||||||
|
intptr_t oem[4];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set the swap interval for this surface.
|
||||||
|
*
|
||||||
|
* Returns 0 on success or -errno on error.
|
||||||
|
*/
|
||||||
|
int (*setSwapInterval)(struct ANativeWindow *window,
|
||||||
|
int interval);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Hook called by EGL to acquire a buffer. After this call, the buffer
|
||||||
|
* is not locked, so its content cannot be modified. This call may block if
|
||||||
|
* no buffers are available.
|
||||||
|
*
|
||||||
|
* The window holds a reference to the buffer between dequeueBuffer and
|
||||||
|
* either queueBuffer or cancelBuffer, so clients only need their own
|
||||||
|
* reference if they might use the buffer after queueing or canceling it.
|
||||||
|
* Holding a reference to a buffer after queueing or canceling it is only
|
||||||
|
* allowed if a specific buffer count has been set.
|
||||||
|
*
|
||||||
|
* Returns 0 on success or -errno on error.
|
||||||
|
*
|
||||||
|
* XXX: This function is deprecated. It will continue to work for some
|
||||||
|
* time for binary compatibility, but the new dequeueBuffer function that
|
||||||
|
* outputs a fence file descriptor should be used in its place.
|
||||||
|
*/
|
||||||
|
int (*dequeueBuffer_DEPRECATED)(struct ANativeWindow *window,
|
||||||
|
struct ANativeWindowBuffer **buffer);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* hook called by EGL to lock a buffer. This MUST be called before modifying
|
||||||
|
* the content of a buffer. The buffer must have been acquired with
|
||||||
|
* dequeueBuffer first.
|
||||||
|
*
|
||||||
|
* Returns 0 on success or -errno on error.
|
||||||
|
*
|
||||||
|
* XXX: This function is deprecated. It will continue to work for some
|
||||||
|
* time for binary compatibility, but it is essentially a no-op, and calls
|
||||||
|
* to it should be removed.
|
||||||
|
*/
|
||||||
|
int (*lockBuffer_DEPRECATED)(struct ANativeWindow *window,
|
||||||
|
struct ANativeWindowBuffer *buffer);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Hook called by EGL when modifications to the render buffer are done.
|
||||||
|
* This unlocks and post the buffer.
|
||||||
|
*
|
||||||
|
* The window holds a reference to the buffer between dequeueBuffer and
|
||||||
|
* either queueBuffer or cancelBuffer, so clients only need their own
|
||||||
|
* reference if they might use the buffer after queueing or canceling it.
|
||||||
|
* Holding a reference to a buffer after queueing or canceling it is only
|
||||||
|
* allowed if a specific buffer count has been set.
|
||||||
|
*
|
||||||
|
* Buffers MUST be queued in the same order than they were dequeued.
|
||||||
|
*
|
||||||
|
* Returns 0 on success or -errno on error.
|
||||||
|
*
|
||||||
|
* XXX: This function is deprecated. It will continue to work for some
|
||||||
|
* time for binary compatibility, but the new queueBuffer function that
|
||||||
|
* takes a fence file descriptor should be used in its place (pass a value
|
||||||
|
* of -1 for the fence file descriptor if there is no valid one to pass).
|
||||||
|
*/
|
||||||
|
int (*queueBuffer_DEPRECATED)(struct ANativeWindow *window,
|
||||||
|
struct ANativeWindowBuffer *buffer);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* hook used to retrieve information about the native window.
|
||||||
|
*
|
||||||
|
* Returns 0 on success or -errno on error.
|
||||||
|
*/
|
||||||
|
int (*query)(const struct ANativeWindow *window,
|
||||||
|
int what, int *value);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* hook used to perform various operations on the surface.
|
||||||
|
* (*perform)() is a generic mechanism to add functionality to
|
||||||
|
* ANativeWindow while keeping backward binary compatibility.
|
||||||
|
*
|
||||||
|
* DO NOT CALL THIS HOOK DIRECTLY. Instead, use the helper functions
|
||||||
|
* defined below.
|
||||||
|
*
|
||||||
|
* (*perform)() returns -ENOENT if the 'what' parameter is not supported
|
||||||
|
* by the surface's implementation.
|
||||||
|
*
|
||||||
|
* See above for a list of valid operations, such as
|
||||||
|
* NATIVE_WINDOW_SET_USAGE or NATIVE_WINDOW_CONNECT
|
||||||
|
*/
|
||||||
|
int (*perform)(struct ANativeWindow *window,
|
||||||
|
int operation, ...);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Hook used to cancel a buffer that has been dequeued.
|
||||||
|
* No synchronization is performed between dequeue() and cancel(), so
|
||||||
|
* either external synchronization is needed, or these functions must be
|
||||||
|
* called from the same thread.
|
||||||
|
*
|
||||||
|
* The window holds a reference to the buffer between dequeueBuffer and
|
||||||
|
* either queueBuffer or cancelBuffer, so clients only need their own
|
||||||
|
* reference if they might use the buffer after queueing or canceling it.
|
||||||
|
* Holding a reference to a buffer after queueing or canceling it is only
|
||||||
|
* allowed if a specific buffer count has been set.
|
||||||
|
*
|
||||||
|
* XXX: This function is deprecated. It will continue to work for some
|
||||||
|
* time for binary compatibility, but the new cancelBuffer function that
|
||||||
|
* takes a fence file descriptor should be used in its place (pass a value
|
||||||
|
* of -1 for the fence file descriptor if there is no valid one to pass).
|
||||||
|
*/
|
||||||
|
int (*cancelBuffer_DEPRECATED)(struct ANativeWindow *window,
|
||||||
|
struct ANativeWindowBuffer *buffer);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Hook called by EGL to acquire a buffer. This call may block if no
|
||||||
|
* buffers are available.
|
||||||
|
*
|
||||||
|
* The window holds a reference to the buffer between dequeueBuffer and
|
||||||
|
* either queueBuffer or cancelBuffer, so clients only need their own
|
||||||
|
* reference if they might use the buffer after queueing or canceling it.
|
||||||
|
* Holding a reference to a buffer after queueing or canceling it is only
|
||||||
|
* allowed if a specific buffer count has been set.
|
||||||
|
*
|
||||||
|
* The libsync fence file descriptor returned in the int pointed to by the
|
||||||
|
* fenceFd argument will refer to the fence that must signal before the
|
||||||
|
* dequeued buffer may be written to. A value of -1 indicates that the
|
||||||
|
* caller may access the buffer immediately without waiting on a fence. If
|
||||||
|
* a valid file descriptor is returned (i.e. any value except -1) then the
|
||||||
|
* caller is responsible for closing the file descriptor.
|
||||||
|
*
|
||||||
|
* Returns 0 on success or -errno on error.
|
||||||
|
*/
|
||||||
|
int (*dequeueBuffer)(struct ANativeWindow *window,
|
||||||
|
struct ANativeWindowBuffer **buffer, int *fenceFd);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Hook called by EGL when modifications to the render buffer are done.
|
||||||
|
* This unlocks and post the buffer.
|
||||||
|
*
|
||||||
|
* The window holds a reference to the buffer between dequeueBuffer and
|
||||||
|
* either queueBuffer or cancelBuffer, so clients only need their own
|
||||||
|
* reference if they might use the buffer after queueing or canceling it.
|
||||||
|
* Holding a reference to a buffer after queueing or canceling it is only
|
||||||
|
* allowed if a specific buffer count has been set.
|
||||||
|
*
|
||||||
|
* The fenceFd argument specifies a libsync fence file descriptor for a
|
||||||
|
* fence that must signal before the buffer can be accessed. If the buffer
|
||||||
|
* can be accessed immediately then a value of -1 should be used. The
|
||||||
|
* caller must not use the file descriptor after it is passed to
|
||||||
|
* queueBuffer, and the ANativeWindow implementation is responsible for
|
||||||
|
* closing it.
|
||||||
|
*
|
||||||
|
* Returns 0 on success or -errno on error.
|
||||||
|
*/
|
||||||
|
int (*queueBuffer)(struct ANativeWindow *window,
|
||||||
|
struct ANativeWindowBuffer *buffer, int fenceFd);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Hook used to cancel a buffer that has been dequeued.
|
||||||
|
* No synchronization is performed between dequeue() and cancel(), so
|
||||||
|
* either external synchronization is needed, or these functions must be
|
||||||
|
* called from the same thread.
|
||||||
|
*
|
||||||
|
* The window holds a reference to the buffer between dequeueBuffer and
|
||||||
|
* either queueBuffer or cancelBuffer, so clients only need their own
|
||||||
|
* reference if they might use the buffer after queueing or canceling it.
|
||||||
|
* Holding a reference to a buffer after queueing or canceling it is only
|
||||||
|
* allowed if a specific buffer count has been set.
|
||||||
|
*
|
||||||
|
* The fenceFd argument specifies a libsync fence file decsriptor for a
|
||||||
|
* fence that must signal before the buffer can be accessed. If the buffer
|
||||||
|
* can be accessed immediately then a value of -1 should be used.
|
||||||
|
*
|
||||||
|
* Note that if the client has not waited on the fence that was returned
|
||||||
|
* from dequeueBuffer, that same fence should be passed to cancelBuffer to
|
||||||
|
* ensure that future uses of the buffer are preceded by a wait on that
|
||||||
|
* fence. The caller must not use the file descriptor after it is passed
|
||||||
|
* to cancelBuffer, and the ANativeWindow implementation is responsible for
|
||||||
|
* closing it.
|
||||||
|
*
|
||||||
|
* Returns 0 on success or -errno on error.
|
||||||
|
*/
|
||||||
|
int (*cancelBuffer)(struct ANativeWindow *window,
|
||||||
|
struct ANativeWindowBuffer *buffer, int fenceFd);
|
||||||
|
};
|
||||||
@@ -0,0 +1,404 @@
|
|||||||
|
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
// This file is generated.
|
||||||
|
#include "vulkan_wrapper.h"
|
||||||
|
#include <dlfcn.h>
|
||||||
|
|
||||||
|
int InitVulkan(void) {
|
||||||
|
void* libvulkan = dlopen("libvulkan.so", RTLD_NOW | RTLD_LOCAL);
|
||||||
|
if (!libvulkan)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// Vulkan supported, set function addresses
|
||||||
|
vkCreateInstance = reinterpret_cast<PFN_vkCreateInstance>(dlsym(libvulkan, "vkCreateInstance"));
|
||||||
|
vkDestroyInstance = reinterpret_cast<PFN_vkDestroyInstance>(dlsym(libvulkan, "vkDestroyInstance"));
|
||||||
|
vkEnumeratePhysicalDevices = reinterpret_cast<PFN_vkEnumeratePhysicalDevices>(dlsym(libvulkan, "vkEnumeratePhysicalDevices"));
|
||||||
|
vkGetPhysicalDeviceFeatures = reinterpret_cast<PFN_vkGetPhysicalDeviceFeatures>(dlsym(libvulkan, "vkGetPhysicalDeviceFeatures"));
|
||||||
|
vkGetPhysicalDeviceFormatProperties = reinterpret_cast<PFN_vkGetPhysicalDeviceFormatProperties>(dlsym(libvulkan, "vkGetPhysicalDeviceFormatProperties"));
|
||||||
|
vkGetPhysicalDeviceImageFormatProperties = reinterpret_cast<PFN_vkGetPhysicalDeviceImageFormatProperties>(dlsym(libvulkan, "vkGetPhysicalDeviceImageFormatProperties"));
|
||||||
|
vkGetPhysicalDeviceProperties = reinterpret_cast<PFN_vkGetPhysicalDeviceProperties>(dlsym(libvulkan, "vkGetPhysicalDeviceProperties"));
|
||||||
|
vkGetPhysicalDeviceQueueFamilyProperties = reinterpret_cast<PFN_vkGetPhysicalDeviceQueueFamilyProperties>(dlsym(libvulkan, "vkGetPhysicalDeviceQueueFamilyProperties"));
|
||||||
|
vkGetPhysicalDeviceMemoryProperties = reinterpret_cast<PFN_vkGetPhysicalDeviceMemoryProperties>(dlsym(libvulkan, "vkGetPhysicalDeviceMemoryProperties"));
|
||||||
|
vkGetInstanceProcAddr = reinterpret_cast<PFN_vkGetInstanceProcAddr>(dlsym(libvulkan, "vkGetInstanceProcAddr"));
|
||||||
|
vkGetDeviceProcAddr = reinterpret_cast<PFN_vkGetDeviceProcAddr>(dlsym(libvulkan, "vkGetDeviceProcAddr"));
|
||||||
|
vkCreateDevice = reinterpret_cast<PFN_vkCreateDevice>(dlsym(libvulkan, "vkCreateDevice"));
|
||||||
|
vkDestroyDevice = reinterpret_cast<PFN_vkDestroyDevice>(dlsym(libvulkan, "vkDestroyDevice"));
|
||||||
|
vkEnumerateInstanceExtensionProperties = reinterpret_cast<PFN_vkEnumerateInstanceExtensionProperties>(dlsym(libvulkan, "vkEnumerateInstanceExtensionProperties"));
|
||||||
|
vkEnumerateDeviceExtensionProperties = reinterpret_cast<PFN_vkEnumerateDeviceExtensionProperties>(dlsym(libvulkan, "vkEnumerateDeviceExtensionProperties"));
|
||||||
|
vkEnumerateInstanceLayerProperties = reinterpret_cast<PFN_vkEnumerateInstanceLayerProperties>(dlsym(libvulkan, "vkEnumerateInstanceLayerProperties"));
|
||||||
|
vkEnumerateDeviceLayerProperties = reinterpret_cast<PFN_vkEnumerateDeviceLayerProperties>(dlsym(libvulkan, "vkEnumerateDeviceLayerProperties"));
|
||||||
|
vkGetDeviceQueue = reinterpret_cast<PFN_vkGetDeviceQueue>(dlsym(libvulkan, "vkGetDeviceQueue"));
|
||||||
|
vkQueueSubmit = reinterpret_cast<PFN_vkQueueSubmit>(dlsym(libvulkan, "vkQueueSubmit"));
|
||||||
|
vkQueueWaitIdle = reinterpret_cast<PFN_vkQueueWaitIdle>(dlsym(libvulkan, "vkQueueWaitIdle"));
|
||||||
|
vkDeviceWaitIdle = reinterpret_cast<PFN_vkDeviceWaitIdle>(dlsym(libvulkan, "vkDeviceWaitIdle"));
|
||||||
|
vkAllocateMemory = reinterpret_cast<PFN_vkAllocateMemory>(dlsym(libvulkan, "vkAllocateMemory"));
|
||||||
|
vkFreeMemory = reinterpret_cast<PFN_vkFreeMemory>(dlsym(libvulkan, "vkFreeMemory"));
|
||||||
|
vkMapMemory = reinterpret_cast<PFN_vkMapMemory>(dlsym(libvulkan, "vkMapMemory"));
|
||||||
|
vkUnmapMemory = reinterpret_cast<PFN_vkUnmapMemory>(dlsym(libvulkan, "vkUnmapMemory"));
|
||||||
|
vkFlushMappedMemoryRanges = reinterpret_cast<PFN_vkFlushMappedMemoryRanges>(dlsym(libvulkan, "vkFlushMappedMemoryRanges"));
|
||||||
|
vkInvalidateMappedMemoryRanges = reinterpret_cast<PFN_vkInvalidateMappedMemoryRanges>(dlsym(libvulkan, "vkInvalidateMappedMemoryRanges"));
|
||||||
|
vkGetDeviceMemoryCommitment = reinterpret_cast<PFN_vkGetDeviceMemoryCommitment>(dlsym(libvulkan, "vkGetDeviceMemoryCommitment"));
|
||||||
|
vkBindBufferMemory = reinterpret_cast<PFN_vkBindBufferMemory>(dlsym(libvulkan, "vkBindBufferMemory"));
|
||||||
|
vkBindImageMemory = reinterpret_cast<PFN_vkBindImageMemory>(dlsym(libvulkan, "vkBindImageMemory"));
|
||||||
|
vkGetBufferMemoryRequirements = reinterpret_cast<PFN_vkGetBufferMemoryRequirements>(dlsym(libvulkan, "vkGetBufferMemoryRequirements"));
|
||||||
|
vkGetImageMemoryRequirements = reinterpret_cast<PFN_vkGetImageMemoryRequirements>(dlsym(libvulkan, "vkGetImageMemoryRequirements"));
|
||||||
|
vkGetImageSparseMemoryRequirements = reinterpret_cast<PFN_vkGetImageSparseMemoryRequirements>(dlsym(libvulkan, "vkGetImageSparseMemoryRequirements"));
|
||||||
|
vkGetPhysicalDeviceSparseImageFormatProperties = reinterpret_cast<PFN_vkGetPhysicalDeviceSparseImageFormatProperties>(dlsym(libvulkan, "vkGetPhysicalDeviceSparseImageFormatProperties"));
|
||||||
|
vkQueueBindSparse = reinterpret_cast<PFN_vkQueueBindSparse>(dlsym(libvulkan, "vkQueueBindSparse"));
|
||||||
|
vkCreateFence = reinterpret_cast<PFN_vkCreateFence>(dlsym(libvulkan, "vkCreateFence"));
|
||||||
|
vkDestroyFence = reinterpret_cast<PFN_vkDestroyFence>(dlsym(libvulkan, "vkDestroyFence"));
|
||||||
|
vkResetFences = reinterpret_cast<PFN_vkResetFences>(dlsym(libvulkan, "vkResetFences"));
|
||||||
|
vkGetFenceStatus = reinterpret_cast<PFN_vkGetFenceStatus>(dlsym(libvulkan, "vkGetFenceStatus"));
|
||||||
|
vkWaitForFences = reinterpret_cast<PFN_vkWaitForFences>(dlsym(libvulkan, "vkWaitForFences"));
|
||||||
|
vkCreateSemaphore = reinterpret_cast<PFN_vkCreateSemaphore>(dlsym(libvulkan, "vkCreateSemaphore"));
|
||||||
|
vkDestroySemaphore = reinterpret_cast<PFN_vkDestroySemaphore>(dlsym(libvulkan, "vkDestroySemaphore"));
|
||||||
|
vkCreateEvent = reinterpret_cast<PFN_vkCreateEvent>(dlsym(libvulkan, "vkCreateEvent"));
|
||||||
|
vkDestroyEvent = reinterpret_cast<PFN_vkDestroyEvent>(dlsym(libvulkan, "vkDestroyEvent"));
|
||||||
|
vkGetEventStatus = reinterpret_cast<PFN_vkGetEventStatus>(dlsym(libvulkan, "vkGetEventStatus"));
|
||||||
|
vkSetEvent = reinterpret_cast<PFN_vkSetEvent>(dlsym(libvulkan, "vkSetEvent"));
|
||||||
|
vkResetEvent = reinterpret_cast<PFN_vkResetEvent>(dlsym(libvulkan, "vkResetEvent"));
|
||||||
|
vkCreateQueryPool = reinterpret_cast<PFN_vkCreateQueryPool>(dlsym(libvulkan, "vkCreateQueryPool"));
|
||||||
|
vkDestroyQueryPool = reinterpret_cast<PFN_vkDestroyQueryPool>(dlsym(libvulkan, "vkDestroyQueryPool"));
|
||||||
|
vkGetQueryPoolResults = reinterpret_cast<PFN_vkGetQueryPoolResults>(dlsym(libvulkan, "vkGetQueryPoolResults"));
|
||||||
|
vkCreateBuffer = reinterpret_cast<PFN_vkCreateBuffer>(dlsym(libvulkan, "vkCreateBuffer"));
|
||||||
|
vkDestroyBuffer = reinterpret_cast<PFN_vkDestroyBuffer>(dlsym(libvulkan, "vkDestroyBuffer"));
|
||||||
|
vkCreateBufferView = reinterpret_cast<PFN_vkCreateBufferView>(dlsym(libvulkan, "vkCreateBufferView"));
|
||||||
|
vkDestroyBufferView = reinterpret_cast<PFN_vkDestroyBufferView>(dlsym(libvulkan, "vkDestroyBufferView"));
|
||||||
|
vkCreateImage = reinterpret_cast<PFN_vkCreateImage>(dlsym(libvulkan, "vkCreateImage"));
|
||||||
|
vkDestroyImage = reinterpret_cast<PFN_vkDestroyImage>(dlsym(libvulkan, "vkDestroyImage"));
|
||||||
|
vkGetImageSubresourceLayout = reinterpret_cast<PFN_vkGetImageSubresourceLayout>(dlsym(libvulkan, "vkGetImageSubresourceLayout"));
|
||||||
|
vkCreateImageView = reinterpret_cast<PFN_vkCreateImageView>(dlsym(libvulkan, "vkCreateImageView"));
|
||||||
|
vkDestroyImageView = reinterpret_cast<PFN_vkDestroyImageView>(dlsym(libvulkan, "vkDestroyImageView"));
|
||||||
|
vkCreateShaderModule = reinterpret_cast<PFN_vkCreateShaderModule>(dlsym(libvulkan, "vkCreateShaderModule"));
|
||||||
|
vkDestroyShaderModule = reinterpret_cast<PFN_vkDestroyShaderModule>(dlsym(libvulkan, "vkDestroyShaderModule"));
|
||||||
|
vkCreatePipelineCache = reinterpret_cast<PFN_vkCreatePipelineCache>(dlsym(libvulkan, "vkCreatePipelineCache"));
|
||||||
|
vkDestroyPipelineCache = reinterpret_cast<PFN_vkDestroyPipelineCache>(dlsym(libvulkan, "vkDestroyPipelineCache"));
|
||||||
|
vkGetPipelineCacheData = reinterpret_cast<PFN_vkGetPipelineCacheData>(dlsym(libvulkan, "vkGetPipelineCacheData"));
|
||||||
|
vkMergePipelineCaches = reinterpret_cast<PFN_vkMergePipelineCaches>(dlsym(libvulkan, "vkMergePipelineCaches"));
|
||||||
|
vkCreateGraphicsPipelines = reinterpret_cast<PFN_vkCreateGraphicsPipelines>(dlsym(libvulkan, "vkCreateGraphicsPipelines"));
|
||||||
|
vkCreateComputePipelines = reinterpret_cast<PFN_vkCreateComputePipelines>(dlsym(libvulkan, "vkCreateComputePipelines"));
|
||||||
|
vkDestroyPipeline = reinterpret_cast<PFN_vkDestroyPipeline>(dlsym(libvulkan, "vkDestroyPipeline"));
|
||||||
|
vkCreatePipelineLayout = reinterpret_cast<PFN_vkCreatePipelineLayout>(dlsym(libvulkan, "vkCreatePipelineLayout"));
|
||||||
|
vkDestroyPipelineLayout = reinterpret_cast<PFN_vkDestroyPipelineLayout>(dlsym(libvulkan, "vkDestroyPipelineLayout"));
|
||||||
|
vkCreateSampler = reinterpret_cast<PFN_vkCreateSampler>(dlsym(libvulkan, "vkCreateSampler"));
|
||||||
|
vkDestroySampler = reinterpret_cast<PFN_vkDestroySampler>(dlsym(libvulkan, "vkDestroySampler"));
|
||||||
|
vkCreateDescriptorSetLayout = reinterpret_cast<PFN_vkCreateDescriptorSetLayout>(dlsym(libvulkan, "vkCreateDescriptorSetLayout"));
|
||||||
|
vkDestroyDescriptorSetLayout = reinterpret_cast<PFN_vkDestroyDescriptorSetLayout>(dlsym(libvulkan, "vkDestroyDescriptorSetLayout"));
|
||||||
|
vkCreateDescriptorPool = reinterpret_cast<PFN_vkCreateDescriptorPool>(dlsym(libvulkan, "vkCreateDescriptorPool"));
|
||||||
|
vkDestroyDescriptorPool = reinterpret_cast<PFN_vkDestroyDescriptorPool>(dlsym(libvulkan, "vkDestroyDescriptorPool"));
|
||||||
|
vkResetDescriptorPool = reinterpret_cast<PFN_vkResetDescriptorPool>(dlsym(libvulkan, "vkResetDescriptorPool"));
|
||||||
|
vkAllocateDescriptorSets = reinterpret_cast<PFN_vkAllocateDescriptorSets>(dlsym(libvulkan, "vkAllocateDescriptorSets"));
|
||||||
|
vkFreeDescriptorSets = reinterpret_cast<PFN_vkFreeDescriptorSets>(dlsym(libvulkan, "vkFreeDescriptorSets"));
|
||||||
|
vkUpdateDescriptorSets = reinterpret_cast<PFN_vkUpdateDescriptorSets>(dlsym(libvulkan, "vkUpdateDescriptorSets"));
|
||||||
|
vkCreateFramebuffer = reinterpret_cast<PFN_vkCreateFramebuffer>(dlsym(libvulkan, "vkCreateFramebuffer"));
|
||||||
|
vkDestroyFramebuffer = reinterpret_cast<PFN_vkDestroyFramebuffer>(dlsym(libvulkan, "vkDestroyFramebuffer"));
|
||||||
|
vkCreateRenderPass = reinterpret_cast<PFN_vkCreateRenderPass>(dlsym(libvulkan, "vkCreateRenderPass"));
|
||||||
|
vkDestroyRenderPass = reinterpret_cast<PFN_vkDestroyRenderPass>(dlsym(libvulkan, "vkDestroyRenderPass"));
|
||||||
|
vkGetRenderAreaGranularity = reinterpret_cast<PFN_vkGetRenderAreaGranularity>(dlsym(libvulkan, "vkGetRenderAreaGranularity"));
|
||||||
|
vkCreateCommandPool = reinterpret_cast<PFN_vkCreateCommandPool>(dlsym(libvulkan, "vkCreateCommandPool"));
|
||||||
|
vkDestroyCommandPool = reinterpret_cast<PFN_vkDestroyCommandPool>(dlsym(libvulkan, "vkDestroyCommandPool"));
|
||||||
|
vkResetCommandPool = reinterpret_cast<PFN_vkResetCommandPool>(dlsym(libvulkan, "vkResetCommandPool"));
|
||||||
|
vkAllocateCommandBuffers = reinterpret_cast<PFN_vkAllocateCommandBuffers>(dlsym(libvulkan, "vkAllocateCommandBuffers"));
|
||||||
|
vkFreeCommandBuffers = reinterpret_cast<PFN_vkFreeCommandBuffers>(dlsym(libvulkan, "vkFreeCommandBuffers"));
|
||||||
|
vkBeginCommandBuffer = reinterpret_cast<PFN_vkBeginCommandBuffer>(dlsym(libvulkan, "vkBeginCommandBuffer"));
|
||||||
|
vkEndCommandBuffer = reinterpret_cast<PFN_vkEndCommandBuffer>(dlsym(libvulkan, "vkEndCommandBuffer"));
|
||||||
|
vkResetCommandBuffer = reinterpret_cast<PFN_vkResetCommandBuffer>(dlsym(libvulkan, "vkResetCommandBuffer"));
|
||||||
|
vkCmdBindPipeline = reinterpret_cast<PFN_vkCmdBindPipeline>(dlsym(libvulkan, "vkCmdBindPipeline"));
|
||||||
|
vkCmdSetViewport = reinterpret_cast<PFN_vkCmdSetViewport>(dlsym(libvulkan, "vkCmdSetViewport"));
|
||||||
|
vkCmdSetScissor = reinterpret_cast<PFN_vkCmdSetScissor>(dlsym(libvulkan, "vkCmdSetScissor"));
|
||||||
|
vkCmdSetLineWidth = reinterpret_cast<PFN_vkCmdSetLineWidth>(dlsym(libvulkan, "vkCmdSetLineWidth"));
|
||||||
|
vkCmdSetDepthBias = reinterpret_cast<PFN_vkCmdSetDepthBias>(dlsym(libvulkan, "vkCmdSetDepthBias"));
|
||||||
|
vkCmdSetBlendConstants = reinterpret_cast<PFN_vkCmdSetBlendConstants>(dlsym(libvulkan, "vkCmdSetBlendConstants"));
|
||||||
|
vkCmdSetDepthBounds = reinterpret_cast<PFN_vkCmdSetDepthBounds>(dlsym(libvulkan, "vkCmdSetDepthBounds"));
|
||||||
|
vkCmdSetStencilCompareMask = reinterpret_cast<PFN_vkCmdSetStencilCompareMask>(dlsym(libvulkan, "vkCmdSetStencilCompareMask"));
|
||||||
|
vkCmdSetStencilWriteMask = reinterpret_cast<PFN_vkCmdSetStencilWriteMask>(dlsym(libvulkan, "vkCmdSetStencilWriteMask"));
|
||||||
|
vkCmdSetStencilReference = reinterpret_cast<PFN_vkCmdSetStencilReference>(dlsym(libvulkan, "vkCmdSetStencilReference"));
|
||||||
|
vkCmdBindDescriptorSets = reinterpret_cast<PFN_vkCmdBindDescriptorSets>(dlsym(libvulkan, "vkCmdBindDescriptorSets"));
|
||||||
|
vkCmdBindIndexBuffer = reinterpret_cast<PFN_vkCmdBindIndexBuffer>(dlsym(libvulkan, "vkCmdBindIndexBuffer"));
|
||||||
|
vkCmdBindVertexBuffers = reinterpret_cast<PFN_vkCmdBindVertexBuffers>(dlsym(libvulkan, "vkCmdBindVertexBuffers"));
|
||||||
|
vkCmdDraw = reinterpret_cast<PFN_vkCmdDraw>(dlsym(libvulkan, "vkCmdDraw"));
|
||||||
|
vkCmdDrawIndexed = reinterpret_cast<PFN_vkCmdDrawIndexed>(dlsym(libvulkan, "vkCmdDrawIndexed"));
|
||||||
|
vkCmdDrawIndirect = reinterpret_cast<PFN_vkCmdDrawIndirect>(dlsym(libvulkan, "vkCmdDrawIndirect"));
|
||||||
|
vkCmdDrawIndexedIndirect = reinterpret_cast<PFN_vkCmdDrawIndexedIndirect>(dlsym(libvulkan, "vkCmdDrawIndexedIndirect"));
|
||||||
|
vkCmdDispatch = reinterpret_cast<PFN_vkCmdDispatch>(dlsym(libvulkan, "vkCmdDispatch"));
|
||||||
|
vkCmdDispatchIndirect = reinterpret_cast<PFN_vkCmdDispatchIndirect>(dlsym(libvulkan, "vkCmdDispatchIndirect"));
|
||||||
|
vkCmdCopyBuffer = reinterpret_cast<PFN_vkCmdCopyBuffer>(dlsym(libvulkan, "vkCmdCopyBuffer"));
|
||||||
|
vkCmdCopyImage = reinterpret_cast<PFN_vkCmdCopyImage>(dlsym(libvulkan, "vkCmdCopyImage"));
|
||||||
|
vkCmdBlitImage = reinterpret_cast<PFN_vkCmdBlitImage>(dlsym(libvulkan, "vkCmdBlitImage"));
|
||||||
|
vkCmdCopyBufferToImage = reinterpret_cast<PFN_vkCmdCopyBufferToImage>(dlsym(libvulkan, "vkCmdCopyBufferToImage"));
|
||||||
|
vkCmdCopyImageToBuffer = reinterpret_cast<PFN_vkCmdCopyImageToBuffer>(dlsym(libvulkan, "vkCmdCopyImageToBuffer"));
|
||||||
|
vkCmdUpdateBuffer = reinterpret_cast<PFN_vkCmdUpdateBuffer>(dlsym(libvulkan, "vkCmdUpdateBuffer"));
|
||||||
|
vkCmdFillBuffer = reinterpret_cast<PFN_vkCmdFillBuffer>(dlsym(libvulkan, "vkCmdFillBuffer"));
|
||||||
|
vkCmdClearColorImage = reinterpret_cast<PFN_vkCmdClearColorImage>(dlsym(libvulkan, "vkCmdClearColorImage"));
|
||||||
|
vkCmdClearDepthStencilImage = reinterpret_cast<PFN_vkCmdClearDepthStencilImage>(dlsym(libvulkan, "vkCmdClearDepthStencilImage"));
|
||||||
|
vkCmdClearAttachments = reinterpret_cast<PFN_vkCmdClearAttachments>(dlsym(libvulkan, "vkCmdClearAttachments"));
|
||||||
|
vkCmdResolveImage = reinterpret_cast<PFN_vkCmdResolveImage>(dlsym(libvulkan, "vkCmdResolveImage"));
|
||||||
|
vkCmdSetEvent = reinterpret_cast<PFN_vkCmdSetEvent>(dlsym(libvulkan, "vkCmdSetEvent"));
|
||||||
|
vkCmdResetEvent = reinterpret_cast<PFN_vkCmdResetEvent>(dlsym(libvulkan, "vkCmdResetEvent"));
|
||||||
|
vkCmdWaitEvents = reinterpret_cast<PFN_vkCmdWaitEvents>(dlsym(libvulkan, "vkCmdWaitEvents"));
|
||||||
|
vkCmdPipelineBarrier = reinterpret_cast<PFN_vkCmdPipelineBarrier>(dlsym(libvulkan, "vkCmdPipelineBarrier"));
|
||||||
|
vkCmdBeginQuery = reinterpret_cast<PFN_vkCmdBeginQuery>(dlsym(libvulkan, "vkCmdBeginQuery"));
|
||||||
|
vkCmdEndQuery = reinterpret_cast<PFN_vkCmdEndQuery>(dlsym(libvulkan, "vkCmdEndQuery"));
|
||||||
|
vkCmdResetQueryPool = reinterpret_cast<PFN_vkCmdResetQueryPool>(dlsym(libvulkan, "vkCmdResetQueryPool"));
|
||||||
|
vkCmdWriteTimestamp = reinterpret_cast<PFN_vkCmdWriteTimestamp>(dlsym(libvulkan, "vkCmdWriteTimestamp"));
|
||||||
|
vkCmdCopyQueryPoolResults = reinterpret_cast<PFN_vkCmdCopyQueryPoolResults>(dlsym(libvulkan, "vkCmdCopyQueryPoolResults"));
|
||||||
|
vkCmdPushConstants = reinterpret_cast<PFN_vkCmdPushConstants>(dlsym(libvulkan, "vkCmdPushConstants"));
|
||||||
|
vkCmdBeginRenderPass = reinterpret_cast<PFN_vkCmdBeginRenderPass>(dlsym(libvulkan, "vkCmdBeginRenderPass"));
|
||||||
|
vkCmdNextSubpass = reinterpret_cast<PFN_vkCmdNextSubpass>(dlsym(libvulkan, "vkCmdNextSubpass"));
|
||||||
|
vkCmdEndRenderPass = reinterpret_cast<PFN_vkCmdEndRenderPass>(dlsym(libvulkan, "vkCmdEndRenderPass"));
|
||||||
|
vkCmdExecuteCommands = reinterpret_cast<PFN_vkCmdExecuteCommands>(dlsym(libvulkan, "vkCmdExecuteCommands"));
|
||||||
|
vkDestroySurfaceKHR = reinterpret_cast<PFN_vkDestroySurfaceKHR>(dlsym(libvulkan, "vkDestroySurfaceKHR"));
|
||||||
|
vkGetPhysicalDeviceSurfaceSupportKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfaceSupportKHR>(dlsym(libvulkan, "vkGetPhysicalDeviceSurfaceSupportKHR"));
|
||||||
|
vkGetPhysicalDeviceSurfaceCapabilitiesKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR>(dlsym(libvulkan, "vkGetPhysicalDeviceSurfaceCapabilitiesKHR"));
|
||||||
|
vkGetPhysicalDeviceSurfaceFormatsKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfaceFormatsKHR>(dlsym(libvulkan, "vkGetPhysicalDeviceSurfaceFormatsKHR"));
|
||||||
|
vkGetPhysicalDeviceSurfacePresentModesKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfacePresentModesKHR>(dlsym(libvulkan, "vkGetPhysicalDeviceSurfacePresentModesKHR"));
|
||||||
|
vkCreateSwapchainKHR = reinterpret_cast<PFN_vkCreateSwapchainKHR>(dlsym(libvulkan, "vkCreateSwapchainKHR"));
|
||||||
|
vkDestroySwapchainKHR = reinterpret_cast<PFN_vkDestroySwapchainKHR>(dlsym(libvulkan, "vkDestroySwapchainKHR"));
|
||||||
|
vkGetSwapchainImagesKHR = reinterpret_cast<PFN_vkGetSwapchainImagesKHR>(dlsym(libvulkan, "vkGetSwapchainImagesKHR"));
|
||||||
|
vkAcquireNextImageKHR = reinterpret_cast<PFN_vkAcquireNextImageKHR>(dlsym(libvulkan, "vkAcquireNextImageKHR"));
|
||||||
|
vkQueuePresentKHR = reinterpret_cast<PFN_vkQueuePresentKHR>(dlsym(libvulkan, "vkQueuePresentKHR"));
|
||||||
|
vkGetPhysicalDeviceDisplayPropertiesKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceDisplayPropertiesKHR>(dlsym(libvulkan, "vkGetPhysicalDeviceDisplayPropertiesKHR"));
|
||||||
|
vkGetPhysicalDeviceDisplayPlanePropertiesKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceDisplayPlanePropertiesKHR>(dlsym(libvulkan, "vkGetPhysicalDeviceDisplayPlanePropertiesKHR"));
|
||||||
|
vkGetDisplayPlaneSupportedDisplaysKHR = reinterpret_cast<PFN_vkGetDisplayPlaneSupportedDisplaysKHR>(dlsym(libvulkan, "vkGetDisplayPlaneSupportedDisplaysKHR"));
|
||||||
|
vkGetDisplayModePropertiesKHR = reinterpret_cast<PFN_vkGetDisplayModePropertiesKHR>(dlsym(libvulkan, "vkGetDisplayModePropertiesKHR"));
|
||||||
|
vkCreateDisplayModeKHR = reinterpret_cast<PFN_vkCreateDisplayModeKHR>(dlsym(libvulkan, "vkCreateDisplayModeKHR"));
|
||||||
|
vkGetDisplayPlaneCapabilitiesKHR = reinterpret_cast<PFN_vkGetDisplayPlaneCapabilitiesKHR>(dlsym(libvulkan, "vkGetDisplayPlaneCapabilitiesKHR"));
|
||||||
|
vkCreateDisplayPlaneSurfaceKHR = reinterpret_cast<PFN_vkCreateDisplayPlaneSurfaceKHR>(dlsym(libvulkan, "vkCreateDisplayPlaneSurfaceKHR"));
|
||||||
|
vkCreateSharedSwapchainsKHR = reinterpret_cast<PFN_vkCreateSharedSwapchainsKHR>(dlsym(libvulkan, "vkCreateSharedSwapchainsKHR"));
|
||||||
|
|
||||||
|
#ifdef VK_USE_PLATFORM_XLIB_KHR
|
||||||
|
vkCreateXlibSurfaceKHR = reinterpret_cast<PFN_vkCreateXlibSurfaceKHR>(dlsym(libvulkan, "vkCreateXlibSurfaceKHR"));
|
||||||
|
vkGetPhysicalDeviceXlibPresentationSupportKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR>(dlsym(libvulkan, "vkGetPhysicalDeviceXlibPresentationSupportKHR"));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef VK_USE_PLATFORM_XCB_KHR
|
||||||
|
vkCreateXcbSurfaceKHR = reinterpret_cast<PFN_vkCreateXcbSurfaceKHR>(dlsym(libvulkan, "vkCreateXcbSurfaceKHR"));
|
||||||
|
vkGetPhysicalDeviceXcbPresentationSupportKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR>(dlsym(libvulkan, "vkGetPhysicalDeviceXcbPresentationSupportKHR"));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef VK_USE_PLATFORM_WAYLAND_KHR
|
||||||
|
vkCreateWaylandSurfaceKHR = reinterpret_cast<PFN_vkCreateWaylandSurfaceKHR>(dlsym(libvulkan, "vkCreateWaylandSurfaceKHR"));
|
||||||
|
vkGetPhysicalDeviceWaylandPresentationSupportKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR>(dlsym(libvulkan, "vkGetPhysicalDeviceWaylandPresentationSupportKHR"));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef VK_USE_PLATFORM_MIR_KHR
|
||||||
|
vkCreateMirSurfaceKHR = reinterpret_cast<PFN_vkCreateMirSurfaceKHR>(dlsym(libvulkan, "vkCreateMirSurfaceKHR"));
|
||||||
|
vkGetPhysicalDeviceMirPresentationSupportKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceMirPresentationSupportKHR>(dlsym(libvulkan, "vkGetPhysicalDeviceMirPresentationSupportKHR"));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef VK_USE_PLATFORM_ANDROID_KHR
|
||||||
|
vkCreateAndroidSurfaceKHR = reinterpret_cast<PFN_vkCreateAndroidSurfaceKHR>(dlsym(libvulkan, "vkCreateAndroidSurfaceKHR"));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef VK_USE_PLATFORM_WIN32_KHR
|
||||||
|
vkCreateWin32SurfaceKHR = reinterpret_cast<PFN_vkCreateWin32SurfaceKHR>(dlsym(libvulkan, "vkCreateWin32SurfaceKHR"));
|
||||||
|
vkGetPhysicalDeviceWin32PresentationSupportKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR>(dlsym(libvulkan, "vkGetPhysicalDeviceWin32PresentationSupportKHR"));
|
||||||
|
#endif
|
||||||
|
#ifdef USE_DEBUG_EXTENTIONS
|
||||||
|
vkCreateDebugReportCallbackEXT = reinterpret_cast<PFN_vkCreateDebugReportCallbackEXT>(dlsym(libvulkan, "vkCreateDebugReportCallbackEXT"));
|
||||||
|
vkDestroyDebugReportCallbackEXT = reinterpret_cast<PFN_vkDestroyDebugReportCallbackEXT>(dlsym(libvulkan, "vkDestroyDebugReportCallbackEXT"));
|
||||||
|
vkDebugReportMessageEXT = reinterpret_cast<PFN_vkDebugReportMessageEXT>(dlsym(libvulkan, "vkDebugReportMessageEXT"));
|
||||||
|
#endif
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No Vulkan support, do not set function addresses
|
||||||
|
PFN_vkCreateInstance vkCreateInstance;
|
||||||
|
PFN_vkDestroyInstance vkDestroyInstance;
|
||||||
|
PFN_vkEnumeratePhysicalDevices vkEnumeratePhysicalDevices;
|
||||||
|
PFN_vkGetPhysicalDeviceFeatures vkGetPhysicalDeviceFeatures;
|
||||||
|
PFN_vkGetPhysicalDeviceFormatProperties vkGetPhysicalDeviceFormatProperties;
|
||||||
|
PFN_vkGetPhysicalDeviceImageFormatProperties vkGetPhysicalDeviceImageFormatProperties;
|
||||||
|
PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties;
|
||||||
|
PFN_vkGetPhysicalDeviceQueueFamilyProperties vkGetPhysicalDeviceQueueFamilyProperties;
|
||||||
|
PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties;
|
||||||
|
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr;
|
||||||
|
PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr;
|
||||||
|
PFN_vkCreateDevice vkCreateDevice;
|
||||||
|
PFN_vkDestroyDevice vkDestroyDevice;
|
||||||
|
PFN_vkEnumerateInstanceExtensionProperties vkEnumerateInstanceExtensionProperties;
|
||||||
|
PFN_vkEnumerateDeviceExtensionProperties vkEnumerateDeviceExtensionProperties;
|
||||||
|
PFN_vkEnumerateInstanceLayerProperties vkEnumerateInstanceLayerProperties;
|
||||||
|
PFN_vkEnumerateDeviceLayerProperties vkEnumerateDeviceLayerProperties;
|
||||||
|
PFN_vkGetDeviceQueue vkGetDeviceQueue;
|
||||||
|
PFN_vkQueueSubmit vkQueueSubmit;
|
||||||
|
PFN_vkQueueWaitIdle vkQueueWaitIdle;
|
||||||
|
PFN_vkDeviceWaitIdle vkDeviceWaitIdle;
|
||||||
|
PFN_vkAllocateMemory vkAllocateMemory;
|
||||||
|
PFN_vkFreeMemory vkFreeMemory;
|
||||||
|
PFN_vkMapMemory vkMapMemory;
|
||||||
|
PFN_vkUnmapMemory vkUnmapMemory;
|
||||||
|
PFN_vkFlushMappedMemoryRanges vkFlushMappedMemoryRanges;
|
||||||
|
PFN_vkInvalidateMappedMemoryRanges vkInvalidateMappedMemoryRanges;
|
||||||
|
PFN_vkGetDeviceMemoryCommitment vkGetDeviceMemoryCommitment;
|
||||||
|
PFN_vkBindBufferMemory vkBindBufferMemory;
|
||||||
|
PFN_vkBindImageMemory vkBindImageMemory;
|
||||||
|
PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements;
|
||||||
|
PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements;
|
||||||
|
PFN_vkGetImageSparseMemoryRequirements vkGetImageSparseMemoryRequirements;
|
||||||
|
PFN_vkGetPhysicalDeviceSparseImageFormatProperties vkGetPhysicalDeviceSparseImageFormatProperties;
|
||||||
|
PFN_vkQueueBindSparse vkQueueBindSparse;
|
||||||
|
PFN_vkCreateFence vkCreateFence;
|
||||||
|
PFN_vkDestroyFence vkDestroyFence;
|
||||||
|
PFN_vkResetFences vkResetFences;
|
||||||
|
PFN_vkGetFenceStatus vkGetFenceStatus;
|
||||||
|
PFN_vkWaitForFences vkWaitForFences;
|
||||||
|
PFN_vkCreateSemaphore vkCreateSemaphore;
|
||||||
|
PFN_vkDestroySemaphore vkDestroySemaphore;
|
||||||
|
PFN_vkCreateEvent vkCreateEvent;
|
||||||
|
PFN_vkDestroyEvent vkDestroyEvent;
|
||||||
|
PFN_vkGetEventStatus vkGetEventStatus;
|
||||||
|
PFN_vkSetEvent vkSetEvent;
|
||||||
|
PFN_vkResetEvent vkResetEvent;
|
||||||
|
PFN_vkCreateQueryPool vkCreateQueryPool;
|
||||||
|
PFN_vkDestroyQueryPool vkDestroyQueryPool;
|
||||||
|
PFN_vkGetQueryPoolResults vkGetQueryPoolResults;
|
||||||
|
PFN_vkCreateBuffer vkCreateBuffer;
|
||||||
|
PFN_vkDestroyBuffer vkDestroyBuffer;
|
||||||
|
PFN_vkCreateBufferView vkCreateBufferView;
|
||||||
|
PFN_vkDestroyBufferView vkDestroyBufferView;
|
||||||
|
PFN_vkCreateImage vkCreateImage;
|
||||||
|
PFN_vkDestroyImage vkDestroyImage;
|
||||||
|
PFN_vkGetImageSubresourceLayout vkGetImageSubresourceLayout;
|
||||||
|
PFN_vkCreateImageView vkCreateImageView;
|
||||||
|
PFN_vkDestroyImageView vkDestroyImageView;
|
||||||
|
PFN_vkCreateShaderModule vkCreateShaderModule;
|
||||||
|
PFN_vkDestroyShaderModule vkDestroyShaderModule;
|
||||||
|
PFN_vkCreatePipelineCache vkCreatePipelineCache;
|
||||||
|
PFN_vkDestroyPipelineCache vkDestroyPipelineCache;
|
||||||
|
PFN_vkGetPipelineCacheData vkGetPipelineCacheData;
|
||||||
|
PFN_vkMergePipelineCaches vkMergePipelineCaches;
|
||||||
|
PFN_vkCreateGraphicsPipelines vkCreateGraphicsPipelines;
|
||||||
|
PFN_vkCreateComputePipelines vkCreateComputePipelines;
|
||||||
|
PFN_vkDestroyPipeline vkDestroyPipeline;
|
||||||
|
PFN_vkCreatePipelineLayout vkCreatePipelineLayout;
|
||||||
|
PFN_vkDestroyPipelineLayout vkDestroyPipelineLayout;
|
||||||
|
PFN_vkCreateSampler vkCreateSampler;
|
||||||
|
PFN_vkDestroySampler vkDestroySampler;
|
||||||
|
PFN_vkCreateDescriptorSetLayout vkCreateDescriptorSetLayout;
|
||||||
|
PFN_vkDestroyDescriptorSetLayout vkDestroyDescriptorSetLayout;
|
||||||
|
PFN_vkCreateDescriptorPool vkCreateDescriptorPool;
|
||||||
|
PFN_vkDestroyDescriptorPool vkDestroyDescriptorPool;
|
||||||
|
PFN_vkResetDescriptorPool vkResetDescriptorPool;
|
||||||
|
PFN_vkAllocateDescriptorSets vkAllocateDescriptorSets;
|
||||||
|
PFN_vkFreeDescriptorSets vkFreeDescriptorSets;
|
||||||
|
PFN_vkUpdateDescriptorSets vkUpdateDescriptorSets;
|
||||||
|
PFN_vkCreateFramebuffer vkCreateFramebuffer;
|
||||||
|
PFN_vkDestroyFramebuffer vkDestroyFramebuffer;
|
||||||
|
PFN_vkCreateRenderPass vkCreateRenderPass;
|
||||||
|
PFN_vkDestroyRenderPass vkDestroyRenderPass;
|
||||||
|
PFN_vkGetRenderAreaGranularity vkGetRenderAreaGranularity;
|
||||||
|
PFN_vkCreateCommandPool vkCreateCommandPool;
|
||||||
|
PFN_vkDestroyCommandPool vkDestroyCommandPool;
|
||||||
|
PFN_vkResetCommandPool vkResetCommandPool;
|
||||||
|
PFN_vkAllocateCommandBuffers vkAllocateCommandBuffers;
|
||||||
|
PFN_vkFreeCommandBuffers vkFreeCommandBuffers;
|
||||||
|
PFN_vkBeginCommandBuffer vkBeginCommandBuffer;
|
||||||
|
PFN_vkEndCommandBuffer vkEndCommandBuffer;
|
||||||
|
PFN_vkResetCommandBuffer vkResetCommandBuffer;
|
||||||
|
PFN_vkCmdBindPipeline vkCmdBindPipeline;
|
||||||
|
PFN_vkCmdSetViewport vkCmdSetViewport;
|
||||||
|
PFN_vkCmdSetScissor vkCmdSetScissor;
|
||||||
|
PFN_vkCmdSetLineWidth vkCmdSetLineWidth;
|
||||||
|
PFN_vkCmdSetDepthBias vkCmdSetDepthBias;
|
||||||
|
PFN_vkCmdSetBlendConstants vkCmdSetBlendConstants;
|
||||||
|
PFN_vkCmdSetDepthBounds vkCmdSetDepthBounds;
|
||||||
|
PFN_vkCmdSetStencilCompareMask vkCmdSetStencilCompareMask;
|
||||||
|
PFN_vkCmdSetStencilWriteMask vkCmdSetStencilWriteMask;
|
||||||
|
PFN_vkCmdSetStencilReference vkCmdSetStencilReference;
|
||||||
|
PFN_vkCmdBindDescriptorSets vkCmdBindDescriptorSets;
|
||||||
|
PFN_vkCmdBindIndexBuffer vkCmdBindIndexBuffer;
|
||||||
|
PFN_vkCmdBindVertexBuffers vkCmdBindVertexBuffers;
|
||||||
|
PFN_vkCmdDraw vkCmdDraw;
|
||||||
|
PFN_vkCmdDrawIndexed vkCmdDrawIndexed;
|
||||||
|
PFN_vkCmdDrawIndirect vkCmdDrawIndirect;
|
||||||
|
PFN_vkCmdDrawIndexedIndirect vkCmdDrawIndexedIndirect;
|
||||||
|
PFN_vkCmdDispatch vkCmdDispatch;
|
||||||
|
PFN_vkCmdDispatchIndirect vkCmdDispatchIndirect;
|
||||||
|
PFN_vkCmdCopyBuffer vkCmdCopyBuffer;
|
||||||
|
PFN_vkCmdCopyImage vkCmdCopyImage;
|
||||||
|
PFN_vkCmdBlitImage vkCmdBlitImage;
|
||||||
|
PFN_vkCmdCopyBufferToImage vkCmdCopyBufferToImage;
|
||||||
|
PFN_vkCmdCopyImageToBuffer vkCmdCopyImageToBuffer;
|
||||||
|
PFN_vkCmdUpdateBuffer vkCmdUpdateBuffer;
|
||||||
|
PFN_vkCmdFillBuffer vkCmdFillBuffer;
|
||||||
|
PFN_vkCmdClearColorImage vkCmdClearColorImage;
|
||||||
|
PFN_vkCmdClearDepthStencilImage vkCmdClearDepthStencilImage;
|
||||||
|
PFN_vkCmdClearAttachments vkCmdClearAttachments;
|
||||||
|
PFN_vkCmdResolveImage vkCmdResolveImage;
|
||||||
|
PFN_vkCmdSetEvent vkCmdSetEvent;
|
||||||
|
PFN_vkCmdResetEvent vkCmdResetEvent;
|
||||||
|
PFN_vkCmdWaitEvents vkCmdWaitEvents;
|
||||||
|
PFN_vkCmdPipelineBarrier vkCmdPipelineBarrier;
|
||||||
|
PFN_vkCmdBeginQuery vkCmdBeginQuery;
|
||||||
|
PFN_vkCmdEndQuery vkCmdEndQuery;
|
||||||
|
PFN_vkCmdResetQueryPool vkCmdResetQueryPool;
|
||||||
|
PFN_vkCmdWriteTimestamp vkCmdWriteTimestamp;
|
||||||
|
PFN_vkCmdCopyQueryPoolResults vkCmdCopyQueryPoolResults;
|
||||||
|
PFN_vkCmdPushConstants vkCmdPushConstants;
|
||||||
|
PFN_vkCmdBeginRenderPass vkCmdBeginRenderPass;
|
||||||
|
PFN_vkCmdNextSubpass vkCmdNextSubpass;
|
||||||
|
PFN_vkCmdEndRenderPass vkCmdEndRenderPass;
|
||||||
|
PFN_vkCmdExecuteCommands vkCmdExecuteCommands;
|
||||||
|
PFN_vkDestroySurfaceKHR vkDestroySurfaceKHR;
|
||||||
|
PFN_vkGetPhysicalDeviceSurfaceSupportKHR vkGetPhysicalDeviceSurfaceSupportKHR;
|
||||||
|
PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR vkGetPhysicalDeviceSurfaceCapabilitiesKHR;
|
||||||
|
PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vkGetPhysicalDeviceSurfaceFormatsKHR;
|
||||||
|
PFN_vkGetPhysicalDeviceSurfacePresentModesKHR vkGetPhysicalDeviceSurfacePresentModesKHR;
|
||||||
|
PFN_vkCreateSwapchainKHR vkCreateSwapchainKHR;
|
||||||
|
PFN_vkDestroySwapchainKHR vkDestroySwapchainKHR;
|
||||||
|
PFN_vkGetSwapchainImagesKHR vkGetSwapchainImagesKHR;
|
||||||
|
PFN_vkAcquireNextImageKHR vkAcquireNextImageKHR;
|
||||||
|
PFN_vkQueuePresentKHR vkQueuePresentKHR;
|
||||||
|
PFN_vkGetPhysicalDeviceDisplayPropertiesKHR vkGetPhysicalDeviceDisplayPropertiesKHR;
|
||||||
|
PFN_vkGetPhysicalDeviceDisplayPlanePropertiesKHR vkGetPhysicalDeviceDisplayPlanePropertiesKHR;
|
||||||
|
PFN_vkGetDisplayPlaneSupportedDisplaysKHR vkGetDisplayPlaneSupportedDisplaysKHR;
|
||||||
|
PFN_vkGetDisplayModePropertiesKHR vkGetDisplayModePropertiesKHR;
|
||||||
|
PFN_vkCreateDisplayModeKHR vkCreateDisplayModeKHR;
|
||||||
|
PFN_vkGetDisplayPlaneCapabilitiesKHR vkGetDisplayPlaneCapabilitiesKHR;
|
||||||
|
PFN_vkCreateDisplayPlaneSurfaceKHR vkCreateDisplayPlaneSurfaceKHR;
|
||||||
|
PFN_vkCreateSharedSwapchainsKHR vkCreateSharedSwapchainsKHR;
|
||||||
|
|
||||||
|
#ifdef VK_USE_PLATFORM_XLIB_KHR
|
||||||
|
PFN_vkCreateXlibSurfaceKHR vkCreateXlibSurfaceKHR;
|
||||||
|
PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR vkGetPhysicalDeviceXlibPresentationSupportKHR;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef VK_USE_PLATFORM_XCB_KHR
|
||||||
|
PFN_vkCreateXcbSurfaceKHR vkCreateXcbSurfaceKHR;
|
||||||
|
PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR vkGetPhysicalDeviceXcbPresentationSupportKHR;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef VK_USE_PLATFORM_WAYLAND_KHR
|
||||||
|
PFN_vkCreateWaylandSurfaceKHR vkCreateWaylandSurfaceKHR;
|
||||||
|
PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR vkGetPhysicalDeviceWaylandPresentationSupportKHR;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef VK_USE_PLATFORM_MIR_KHR
|
||||||
|
PFN_vkCreateMirSurfaceKHR vkCreateMirSurfaceKHR;
|
||||||
|
PFN_vkGetPhysicalDeviceMirPresentationSupportKHR vkGetPhysicalDeviceMirPresentationSupportKHR;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef VK_USE_PLATFORM_ANDROID_KHR
|
||||||
|
PFN_vkCreateAndroidSurfaceKHR vkCreateAndroidSurfaceKHR;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef VK_USE_PLATFORM_WIN32_KHR
|
||||||
|
PFN_vkCreateWin32SurfaceKHR vkCreateWin32SurfaceKHR;
|
||||||
|
PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR vkGetPhysicalDeviceWin32PresentationSupportKHR;
|
||||||
|
#endif
|
||||||
|
PFN_vkCreateDebugReportCallbackEXT vkCreateDebugReportCallbackEXT;
|
||||||
|
PFN_vkDestroyDebugReportCallbackEXT vkDestroyDebugReportCallbackEXT;
|
||||||
|
PFN_vkDebugReportMessageEXT vkDebugReportMessageEXT;
|
||||||
|
|
||||||
@@ -0,0 +1,236 @@
|
|||||||
|
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// This file is generated.
|
||||||
|
#ifndef VULKAN_WRAPPER_H
|
||||||
|
#define VULKAN_WRAPPER_H
|
||||||
|
|
||||||
|
#define VK_NO_PROTOTYPES 1
|
||||||
|
#include <vulkan/vulkan.h>
|
||||||
|
|
||||||
|
/* Initialize the Vulkan function pointer variables declared in this header.
|
||||||
|
* Returns 0 if vulkan is not available, non-zero if it is available.
|
||||||
|
*/
|
||||||
|
int InitVulkan(void);
|
||||||
|
|
||||||
|
// VK_core
|
||||||
|
extern PFN_vkCreateInstance vkCreateInstance;
|
||||||
|
extern PFN_vkDestroyInstance vkDestroyInstance;
|
||||||
|
extern PFN_vkEnumeratePhysicalDevices vkEnumeratePhysicalDevices;
|
||||||
|
extern PFN_vkGetPhysicalDeviceFeatures vkGetPhysicalDeviceFeatures;
|
||||||
|
extern PFN_vkGetPhysicalDeviceFormatProperties vkGetPhysicalDeviceFormatProperties;
|
||||||
|
extern PFN_vkGetPhysicalDeviceImageFormatProperties vkGetPhysicalDeviceImageFormatProperties;
|
||||||
|
extern PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties;
|
||||||
|
extern PFN_vkGetPhysicalDeviceQueueFamilyProperties vkGetPhysicalDeviceQueueFamilyProperties;
|
||||||
|
extern PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties;
|
||||||
|
extern PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr;
|
||||||
|
extern PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr;
|
||||||
|
extern PFN_vkCreateDevice vkCreateDevice;
|
||||||
|
extern PFN_vkDestroyDevice vkDestroyDevice;
|
||||||
|
extern PFN_vkEnumerateInstanceExtensionProperties vkEnumerateInstanceExtensionProperties;
|
||||||
|
extern PFN_vkEnumerateDeviceExtensionProperties vkEnumerateDeviceExtensionProperties;
|
||||||
|
extern PFN_vkEnumerateInstanceLayerProperties vkEnumerateInstanceLayerProperties;
|
||||||
|
extern PFN_vkEnumerateDeviceLayerProperties vkEnumerateDeviceLayerProperties;
|
||||||
|
extern PFN_vkGetDeviceQueue vkGetDeviceQueue;
|
||||||
|
extern PFN_vkQueueSubmit vkQueueSubmit;
|
||||||
|
extern PFN_vkQueueWaitIdle vkQueueWaitIdle;
|
||||||
|
extern PFN_vkDeviceWaitIdle vkDeviceWaitIdle;
|
||||||
|
extern PFN_vkAllocateMemory vkAllocateMemory;
|
||||||
|
extern PFN_vkFreeMemory vkFreeMemory;
|
||||||
|
extern PFN_vkMapMemory vkMapMemory;
|
||||||
|
extern PFN_vkUnmapMemory vkUnmapMemory;
|
||||||
|
extern PFN_vkFlushMappedMemoryRanges vkFlushMappedMemoryRanges;
|
||||||
|
extern PFN_vkInvalidateMappedMemoryRanges vkInvalidateMappedMemoryRanges;
|
||||||
|
extern PFN_vkGetDeviceMemoryCommitment vkGetDeviceMemoryCommitment;
|
||||||
|
extern PFN_vkBindBufferMemory vkBindBufferMemory;
|
||||||
|
extern PFN_vkBindImageMemory vkBindImageMemory;
|
||||||
|
extern PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements;
|
||||||
|
extern PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements;
|
||||||
|
extern PFN_vkGetImageSparseMemoryRequirements vkGetImageSparseMemoryRequirements;
|
||||||
|
extern PFN_vkGetPhysicalDeviceSparseImageFormatProperties vkGetPhysicalDeviceSparseImageFormatProperties;
|
||||||
|
extern PFN_vkQueueBindSparse vkQueueBindSparse;
|
||||||
|
extern PFN_vkCreateFence vkCreateFence;
|
||||||
|
extern PFN_vkDestroyFence vkDestroyFence;
|
||||||
|
extern PFN_vkResetFences vkResetFences;
|
||||||
|
extern PFN_vkGetFenceStatus vkGetFenceStatus;
|
||||||
|
extern PFN_vkWaitForFences vkWaitForFences;
|
||||||
|
extern PFN_vkCreateSemaphore vkCreateSemaphore;
|
||||||
|
extern PFN_vkDestroySemaphore vkDestroySemaphore;
|
||||||
|
extern PFN_vkCreateEvent vkCreateEvent;
|
||||||
|
extern PFN_vkDestroyEvent vkDestroyEvent;
|
||||||
|
extern PFN_vkGetEventStatus vkGetEventStatus;
|
||||||
|
extern PFN_vkSetEvent vkSetEvent;
|
||||||
|
extern PFN_vkResetEvent vkResetEvent;
|
||||||
|
extern PFN_vkCreateQueryPool vkCreateQueryPool;
|
||||||
|
extern PFN_vkDestroyQueryPool vkDestroyQueryPool;
|
||||||
|
extern PFN_vkGetQueryPoolResults vkGetQueryPoolResults;
|
||||||
|
extern PFN_vkCreateBuffer vkCreateBuffer;
|
||||||
|
extern PFN_vkDestroyBuffer vkDestroyBuffer;
|
||||||
|
extern PFN_vkCreateBufferView vkCreateBufferView;
|
||||||
|
extern PFN_vkDestroyBufferView vkDestroyBufferView;
|
||||||
|
extern PFN_vkCreateImage vkCreateImage;
|
||||||
|
extern PFN_vkDestroyImage vkDestroyImage;
|
||||||
|
extern PFN_vkGetImageSubresourceLayout vkGetImageSubresourceLayout;
|
||||||
|
extern PFN_vkCreateImageView vkCreateImageView;
|
||||||
|
extern PFN_vkDestroyImageView vkDestroyImageView;
|
||||||
|
extern PFN_vkCreateShaderModule vkCreateShaderModule;
|
||||||
|
extern PFN_vkDestroyShaderModule vkDestroyShaderModule;
|
||||||
|
extern PFN_vkCreatePipelineCache vkCreatePipelineCache;
|
||||||
|
extern PFN_vkDestroyPipelineCache vkDestroyPipelineCache;
|
||||||
|
extern PFN_vkGetPipelineCacheData vkGetPipelineCacheData;
|
||||||
|
extern PFN_vkMergePipelineCaches vkMergePipelineCaches;
|
||||||
|
extern PFN_vkCreateGraphicsPipelines vkCreateGraphicsPipelines;
|
||||||
|
extern PFN_vkCreateComputePipelines vkCreateComputePipelines;
|
||||||
|
extern PFN_vkDestroyPipeline vkDestroyPipeline;
|
||||||
|
extern PFN_vkCreatePipelineLayout vkCreatePipelineLayout;
|
||||||
|
extern PFN_vkDestroyPipelineLayout vkDestroyPipelineLayout;
|
||||||
|
extern PFN_vkCreateSampler vkCreateSampler;
|
||||||
|
extern PFN_vkDestroySampler vkDestroySampler;
|
||||||
|
extern PFN_vkCreateDescriptorSetLayout vkCreateDescriptorSetLayout;
|
||||||
|
extern PFN_vkDestroyDescriptorSetLayout vkDestroyDescriptorSetLayout;
|
||||||
|
extern PFN_vkCreateDescriptorPool vkCreateDescriptorPool;
|
||||||
|
extern PFN_vkDestroyDescriptorPool vkDestroyDescriptorPool;
|
||||||
|
extern PFN_vkResetDescriptorPool vkResetDescriptorPool;
|
||||||
|
extern PFN_vkAllocateDescriptorSets vkAllocateDescriptorSets;
|
||||||
|
extern PFN_vkFreeDescriptorSets vkFreeDescriptorSets;
|
||||||
|
extern PFN_vkUpdateDescriptorSets vkUpdateDescriptorSets;
|
||||||
|
extern PFN_vkCreateFramebuffer vkCreateFramebuffer;
|
||||||
|
extern PFN_vkDestroyFramebuffer vkDestroyFramebuffer;
|
||||||
|
extern PFN_vkCreateRenderPass vkCreateRenderPass;
|
||||||
|
extern PFN_vkDestroyRenderPass vkDestroyRenderPass;
|
||||||
|
extern PFN_vkGetRenderAreaGranularity vkGetRenderAreaGranularity;
|
||||||
|
extern PFN_vkCreateCommandPool vkCreateCommandPool;
|
||||||
|
extern PFN_vkDestroyCommandPool vkDestroyCommandPool;
|
||||||
|
extern PFN_vkResetCommandPool vkResetCommandPool;
|
||||||
|
extern PFN_vkAllocateCommandBuffers vkAllocateCommandBuffers;
|
||||||
|
extern PFN_vkFreeCommandBuffers vkFreeCommandBuffers;
|
||||||
|
extern PFN_vkBeginCommandBuffer vkBeginCommandBuffer;
|
||||||
|
extern PFN_vkEndCommandBuffer vkEndCommandBuffer;
|
||||||
|
extern PFN_vkResetCommandBuffer vkResetCommandBuffer;
|
||||||
|
extern PFN_vkCmdBindPipeline vkCmdBindPipeline;
|
||||||
|
extern PFN_vkCmdSetViewport vkCmdSetViewport;
|
||||||
|
extern PFN_vkCmdSetScissor vkCmdSetScissor;
|
||||||
|
extern PFN_vkCmdSetLineWidth vkCmdSetLineWidth;
|
||||||
|
extern PFN_vkCmdSetDepthBias vkCmdSetDepthBias;
|
||||||
|
extern PFN_vkCmdSetBlendConstants vkCmdSetBlendConstants;
|
||||||
|
extern PFN_vkCmdSetDepthBounds vkCmdSetDepthBounds;
|
||||||
|
extern PFN_vkCmdSetStencilCompareMask vkCmdSetStencilCompareMask;
|
||||||
|
extern PFN_vkCmdSetStencilWriteMask vkCmdSetStencilWriteMask;
|
||||||
|
extern PFN_vkCmdSetStencilReference vkCmdSetStencilReference;
|
||||||
|
extern PFN_vkCmdBindDescriptorSets vkCmdBindDescriptorSets;
|
||||||
|
extern PFN_vkCmdBindIndexBuffer vkCmdBindIndexBuffer;
|
||||||
|
extern PFN_vkCmdBindVertexBuffers vkCmdBindVertexBuffers;
|
||||||
|
extern PFN_vkCmdDraw vkCmdDraw;
|
||||||
|
extern PFN_vkCmdDrawIndexed vkCmdDrawIndexed;
|
||||||
|
extern PFN_vkCmdDrawIndirect vkCmdDrawIndirect;
|
||||||
|
extern PFN_vkCmdDrawIndexedIndirect vkCmdDrawIndexedIndirect;
|
||||||
|
extern PFN_vkCmdDispatch vkCmdDispatch;
|
||||||
|
extern PFN_vkCmdDispatchIndirect vkCmdDispatchIndirect;
|
||||||
|
extern PFN_vkCmdCopyBuffer vkCmdCopyBuffer;
|
||||||
|
extern PFN_vkCmdCopyImage vkCmdCopyImage;
|
||||||
|
extern PFN_vkCmdBlitImage vkCmdBlitImage;
|
||||||
|
extern PFN_vkCmdCopyBufferToImage vkCmdCopyBufferToImage;
|
||||||
|
extern PFN_vkCmdCopyImageToBuffer vkCmdCopyImageToBuffer;
|
||||||
|
extern PFN_vkCmdUpdateBuffer vkCmdUpdateBuffer;
|
||||||
|
extern PFN_vkCmdFillBuffer vkCmdFillBuffer;
|
||||||
|
extern PFN_vkCmdClearColorImage vkCmdClearColorImage;
|
||||||
|
extern PFN_vkCmdClearDepthStencilImage vkCmdClearDepthStencilImage;
|
||||||
|
extern PFN_vkCmdClearAttachments vkCmdClearAttachments;
|
||||||
|
extern PFN_vkCmdResolveImage vkCmdResolveImage;
|
||||||
|
extern PFN_vkCmdSetEvent vkCmdSetEvent;
|
||||||
|
extern PFN_vkCmdResetEvent vkCmdResetEvent;
|
||||||
|
extern PFN_vkCmdWaitEvents vkCmdWaitEvents;
|
||||||
|
extern PFN_vkCmdPipelineBarrier vkCmdPipelineBarrier;
|
||||||
|
extern PFN_vkCmdBeginQuery vkCmdBeginQuery;
|
||||||
|
extern PFN_vkCmdEndQuery vkCmdEndQuery;
|
||||||
|
extern PFN_vkCmdResetQueryPool vkCmdResetQueryPool;
|
||||||
|
extern PFN_vkCmdWriteTimestamp vkCmdWriteTimestamp;
|
||||||
|
extern PFN_vkCmdCopyQueryPoolResults vkCmdCopyQueryPoolResults;
|
||||||
|
extern PFN_vkCmdPushConstants vkCmdPushConstants;
|
||||||
|
extern PFN_vkCmdBeginRenderPass vkCmdBeginRenderPass;
|
||||||
|
extern PFN_vkCmdNextSubpass vkCmdNextSubpass;
|
||||||
|
extern PFN_vkCmdEndRenderPass vkCmdEndRenderPass;
|
||||||
|
extern PFN_vkCmdExecuteCommands vkCmdExecuteCommands;
|
||||||
|
|
||||||
|
// VK_KHR_surface
|
||||||
|
extern PFN_vkDestroySurfaceKHR vkDestroySurfaceKHR;
|
||||||
|
extern PFN_vkGetPhysicalDeviceSurfaceSupportKHR vkGetPhysicalDeviceSurfaceSupportKHR;
|
||||||
|
extern PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR vkGetPhysicalDeviceSurfaceCapabilitiesKHR;
|
||||||
|
extern PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vkGetPhysicalDeviceSurfaceFormatsKHR;
|
||||||
|
extern PFN_vkGetPhysicalDeviceSurfacePresentModesKHR vkGetPhysicalDeviceSurfacePresentModesKHR;
|
||||||
|
|
||||||
|
// VK_KHR_swapchain
|
||||||
|
extern PFN_vkCreateSwapchainKHR vkCreateSwapchainKHR;
|
||||||
|
extern PFN_vkDestroySwapchainKHR vkDestroySwapchainKHR;
|
||||||
|
extern PFN_vkGetSwapchainImagesKHR vkGetSwapchainImagesKHR;
|
||||||
|
extern PFN_vkAcquireNextImageKHR vkAcquireNextImageKHR;
|
||||||
|
extern PFN_vkQueuePresentKHR vkQueuePresentKHR;
|
||||||
|
|
||||||
|
// VK_KHR_display
|
||||||
|
extern PFN_vkGetPhysicalDeviceDisplayPropertiesKHR vkGetPhysicalDeviceDisplayPropertiesKHR;
|
||||||
|
extern PFN_vkGetPhysicalDeviceDisplayPlanePropertiesKHR vkGetPhysicalDeviceDisplayPlanePropertiesKHR;
|
||||||
|
extern PFN_vkGetDisplayPlaneSupportedDisplaysKHR vkGetDisplayPlaneSupportedDisplaysKHR;
|
||||||
|
extern PFN_vkGetDisplayModePropertiesKHR vkGetDisplayModePropertiesKHR;
|
||||||
|
extern PFN_vkCreateDisplayModeKHR vkCreateDisplayModeKHR;
|
||||||
|
extern PFN_vkGetDisplayPlaneCapabilitiesKHR vkGetDisplayPlaneCapabilitiesKHR;
|
||||||
|
extern PFN_vkCreateDisplayPlaneSurfaceKHR vkCreateDisplayPlaneSurfaceKHR;
|
||||||
|
|
||||||
|
// VK_KHR_display_swapchain
|
||||||
|
extern PFN_vkCreateSharedSwapchainsKHR vkCreateSharedSwapchainsKHR;
|
||||||
|
|
||||||
|
#ifdef VK_USE_PLATFORM_XLIB_KHR
|
||||||
|
// VK_KHR_xlib_surface
|
||||||
|
extern PFN_vkCreateXlibSurfaceKHR vkCreateXlibSurfaceKHR;
|
||||||
|
extern PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR vkGetPhysicalDeviceXlibPresentationSupportKHR;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef VK_USE_PLATFORM_XCB_KHR
|
||||||
|
// VK_KHR_xcb_surface
|
||||||
|
extern PFN_vkCreateXcbSurfaceKHR vkCreateXcbSurfaceKHR;
|
||||||
|
extern PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR vkGetPhysicalDeviceXcbPresentationSupportKHR;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef VK_USE_PLATFORM_WAYLAND_KHR
|
||||||
|
// VK_KHR_wayland_surface
|
||||||
|
extern PFN_vkCreateWaylandSurfaceKHR vkCreateWaylandSurfaceKHR;
|
||||||
|
extern PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR vkGetPhysicalDeviceWaylandPresentationSupportKHR;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef VK_USE_PLATFORM_MIR_KHR
|
||||||
|
// VK_KHR_mir_surface
|
||||||
|
extern PFN_vkCreateMirSurfaceKHR vkCreateMirSurfaceKHR;
|
||||||
|
extern PFN_vkGetPhysicalDeviceMirPresentationSupportKHR vkGetPhysicalDeviceMirPresentationSupportKHR;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef VK_USE_PLATFORM_ANDROID_KHR
|
||||||
|
// VK_KHR_android_surface
|
||||||
|
extern PFN_vkCreateAndroidSurfaceKHR vkCreateAndroidSurfaceKHR;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef VK_USE_PLATFORM_WIN32_KHR
|
||||||
|
// VK_KHR_win32_surface
|
||||||
|
extern PFN_vkCreateWin32SurfaceKHR vkCreateWin32SurfaceKHR;
|
||||||
|
extern PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR vkGetPhysicalDeviceWin32PresentationSupportKHR;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_DEBUG_EXTENTIONS
|
||||||
|
#include <vulkan/vk_sdk_platform.h>
|
||||||
|
// VK_EXT_debug_report
|
||||||
|
extern PFN_vkCreateDebugReportCallbackEXT vkCreateDebugReportCallbackEXT;
|
||||||
|
extern PFN_vkDestroyDebugReportCallbackEXT vkDestroyDebugReportCallbackEXT;
|
||||||
|
extern PFN_vkDebugReportMessageEXT vkDebugReportMessageEXT;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#endif // VULKAN_WRAPPER_H
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
@@ -0,0 +1,7 @@
|
|||||||
|
package org.kenjinx.android
|
||||||
|
|
||||||
|
enum class BackendThreading {
|
||||||
|
Auto,
|
||||||
|
Off,
|
||||||
|
On
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package org.kenjinx.android
|
||||||
|
|
||||||
|
import androidx.activity.ComponentActivity
|
||||||
|
|
||||||
|
abstract class BaseActivity : ComponentActivity() {
|
||||||
|
companion object {
|
||||||
|
val crashHandler = CrashHandler()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
package org.kenjinx.android
|
||||||
|
|
||||||
|
import android.os.Build
|
||||||
|
import java.io.File
|
||||||
|
import java.io.PrintWriter
|
||||||
|
import java.io.StringWriter
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.Date
|
||||||
|
import java.util.Locale
|
||||||
|
import java.lang.Thread.UncaughtExceptionHandler
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles uncaught exceptions in the application and logs them to a file
|
||||||
|
* Location: src/main/java/org/kenjinx/android/CrashHandler.kt
|
||||||
|
*/
|
||||||
|
class CrashHandler : UncaughtExceptionHandler {
|
||||||
|
private var crashLog: StringBuilder = StringBuilder()
|
||||||
|
private val dateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault())
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val LOG_FOLDER = "Logs"
|
||||||
|
private const val CRASH_LOG_FILE = "crash.log"
|
||||||
|
private const val MAX_LOG_SIZE = 5 * 1024 * 1024 // 5MB
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun uncaughtException(t: Thread, e: Throwable) {
|
||||||
|
try {
|
||||||
|
generateCrashLog(t, e)
|
||||||
|
saveCrashLog()
|
||||||
|
} catch (writeError: Exception) {
|
||||||
|
// If we can't write to the crash log, print to system log as fallback
|
||||||
|
writeError.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun generateCrashLog(thread: Thread, throwable: Throwable) {
|
||||||
|
crashLog.apply {
|
||||||
|
append("\n=== Crash Report ===\n")
|
||||||
|
append("Date: ${dateFormat.format(Date())}\n")
|
||||||
|
|
||||||
|
// Device Information
|
||||||
|
append("\n=== Device Info ===\n")
|
||||||
|
append("Device: ${Build.MANUFACTURER} ${Build.MODEL}\n")
|
||||||
|
append("Android Version: ${Build.VERSION.RELEASE} (SDK ${Build.VERSION.SDK_INT})\n")
|
||||||
|
append("Device Fingerprint: ${Build.FINGERPRINT}\n")
|
||||||
|
|
||||||
|
// Thread Information
|
||||||
|
append("\n=== Thread Info ===\n")
|
||||||
|
append("Thread Name: ${thread.name}\n")
|
||||||
|
append("Thread ID: ${thread.id}\n")
|
||||||
|
append("Thread Priority: ${thread.priority}\n")
|
||||||
|
append("Thread State: ${thread.state}\n")
|
||||||
|
|
||||||
|
// Exception Details
|
||||||
|
append("\n=== Exception Info ===\n")
|
||||||
|
append("Exception Class: ${throwable.javaClass.name}\n")
|
||||||
|
append("Exception Message: ${throwable.message}\n")
|
||||||
|
|
||||||
|
// Stack Trace
|
||||||
|
append("\n=== Stack Trace ===\n")
|
||||||
|
val sw = StringWriter()
|
||||||
|
throwable.printStackTrace(PrintWriter(sw))
|
||||||
|
append(sw.toString())
|
||||||
|
|
||||||
|
// Cause (if any)
|
||||||
|
throwable.cause?.let { cause ->
|
||||||
|
append("\n=== Cause ===\n")
|
||||||
|
val causeSw = StringWriter()
|
||||||
|
cause.printStackTrace(PrintWriter(causeSw))
|
||||||
|
append(causeSw.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
append("\n=== End of Crash Report ===\n")
|
||||||
|
append("----------------------------------------\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun saveCrashLog() {
|
||||||
|
val logDir = File(MainActivity.AppPath, LOG_FOLDER).apply {
|
||||||
|
if (!exists()) mkdirs()
|
||||||
|
}
|
||||||
|
|
||||||
|
val crashLogFile = File(logDir, CRASH_LOG_FILE)
|
||||||
|
|
||||||
|
// Rotate log if it's too large
|
||||||
|
if (crashLogFile.exists() && crashLogFile.length() > MAX_LOG_SIZE) {
|
||||||
|
val backupFile = File(logDir, "${CRASH_LOG_FILE}.old")
|
||||||
|
if (backupFile.exists()) backupFile.delete()
|
||||||
|
crashLogFile.renameTo(backupFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the crash log
|
||||||
|
crashLogFile.appendText(crashLog.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,436 @@
|
|||||||
|
package org.kenjinx.android
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Context
|
||||||
|
import android.view.KeyEvent
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.FrameLayout
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.viewinterop.AndroidView
|
||||||
|
import androidx.core.math.MathUtils
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import com.swordfish.radialgamepad.library.RadialGamePad
|
||||||
|
import com.swordfish.radialgamepad.library.config.ButtonConfig
|
||||||
|
import com.swordfish.radialgamepad.library.config.CrossConfig
|
||||||
|
import com.swordfish.radialgamepad.library.config.CrossContentDescription
|
||||||
|
import com.swordfish.radialgamepad.library.config.PrimaryDialConfig
|
||||||
|
import com.swordfish.radialgamepad.library.config.RadialGamePadConfig
|
||||||
|
import com.swordfish.radialgamepad.library.config.SecondaryDialConfig
|
||||||
|
import com.swordfish.radialgamepad.library.event.Event
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
|
import kotlinx.coroutines.flow.catch
|
||||||
|
import kotlinx.coroutines.flow.merge
|
||||||
|
import kotlinx.coroutines.flow.shareIn
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import org.kenjinx.android.viewmodels.MainViewModel
|
||||||
|
import org.kenjinx.android.viewmodels.QuickSettings
|
||||||
|
|
||||||
|
typealias GamePad = RadialGamePad
|
||||||
|
typealias GamePadConfig = RadialGamePadConfig
|
||||||
|
|
||||||
|
// --- Dummy IDs to disconnect legacy L3/R3 (stick double tap/tap) ---
|
||||||
|
private const val DUMMY_LEFT_STICK_PRESS_ID = 10001
|
||||||
|
private const val DUMMY_RIGHT_STICK_PRESS_ID = 10002
|
||||||
|
|
||||||
|
class GameController(var activity: Activity) : IGameController {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private fun init(context: Context, controller: GameController): View {
|
||||||
|
val inflater = LayoutInflater.from(context)
|
||||||
|
val parent = FrameLayout(context)
|
||||||
|
val view = inflater.inflate(R.layout.game_layout, parent, false)
|
||||||
|
|
||||||
|
val leftContainer = view.findViewById<FrameLayout>(R.id.leftcontainer)!!
|
||||||
|
val rightContainer = view.findViewById<FrameLayout>(R.id.rightcontainer)!!
|
||||||
|
|
||||||
|
leftContainer.addView(controller.leftGamePad)
|
||||||
|
rightContainer.addView(controller.rightGamePad)
|
||||||
|
|
||||||
|
leftContainer.addOnLayoutChangeListener { v, _, _, _, _, _, _, _, _ ->
|
||||||
|
val scale = QuickSettings(controller.activity).controllerScale.coerceIn(0.5f, 1.5f)
|
||||||
|
v.pivotX = 0f
|
||||||
|
v.pivotY = v.height.toFloat()
|
||||||
|
v.scaleX = scale
|
||||||
|
v.scaleY = scale
|
||||||
|
}
|
||||||
|
rightContainer.addOnLayoutChangeListener { v, _, _, _, _, _, _, _, _ ->
|
||||||
|
val scale = QuickSettings(controller.activity).controllerScale.coerceIn(0.5f, 1.5f)
|
||||||
|
v.pivotX = v.width.toFloat()
|
||||||
|
v.pivotY = v.height.toFloat()
|
||||||
|
v.scaleX = scale
|
||||||
|
v.scaleY = scale
|
||||||
|
}
|
||||||
|
|
||||||
|
leftContainer.post { leftContainer.requestLayout() }
|
||||||
|
rightContainer.post { rightContainer.requestLayout() }
|
||||||
|
|
||||||
|
return view
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun Compose(viewModel: MainViewModel) {
|
||||||
|
AndroidView(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
factory = { context ->
|
||||||
|
val controller = GameController(viewModel.activity)
|
||||||
|
val c = init(context, controller)
|
||||||
|
viewModel.activity.lifecycleScope.apply {
|
||||||
|
viewModel.activity.lifecycleScope.launch {
|
||||||
|
val events = merge(
|
||||||
|
controller.leftGamePad.events(),
|
||||||
|
controller.rightGamePad.events()
|
||||||
|
).shareIn(viewModel.activity.lifecycleScope, SharingStarted.Lazily)
|
||||||
|
events.safeCollect {
|
||||||
|
controller.handleEvent(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
controller.controllerView = c
|
||||||
|
viewModel.setGameController(controller)
|
||||||
|
controller.setVisible(QuickSettings(viewModel.activity).useVirtualController)
|
||||||
|
c
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private var controllerView: View? = null
|
||||||
|
var leftGamePad: GamePad
|
||||||
|
var rightGamePad: GamePad
|
||||||
|
var controllerId: Int = -1
|
||||||
|
override val isVisible: Boolean
|
||||||
|
get() = controllerView?.isVisible ?: false
|
||||||
|
|
||||||
|
init {
|
||||||
|
val useSwitchLayout = QuickSettings(activity).useSwitchLayout
|
||||||
|
leftGamePad = GamePad(generateConfig(true, useSwitchLayout), 16f, activity)
|
||||||
|
rightGamePad = GamePad(generateConfig(false, useSwitchLayout), 16f, activity)
|
||||||
|
|
||||||
|
leftGamePad.primaryDialMaxSizeDp = 200f
|
||||||
|
rightGamePad.primaryDialMaxSizeDp = 200f
|
||||||
|
|
||||||
|
leftGamePad.gravityX = -1f
|
||||||
|
leftGamePad.gravityY = 1f
|
||||||
|
rightGamePad.gravityX = 1f
|
||||||
|
rightGamePad.gravityY = 1f
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setVisible(isVisible: Boolean) {
|
||||||
|
controllerView?.apply {
|
||||||
|
this.isVisible = isVisible
|
||||||
|
if (isVisible) connect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun connect() {
|
||||||
|
if (controllerId == -1)
|
||||||
|
controllerId = KenjinxNative.inputConnectGamepad(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleEvent(ev: Event) {
|
||||||
|
if (controllerId == -1)
|
||||||
|
controllerId = KenjinxNative.inputConnectGamepad(0)
|
||||||
|
|
||||||
|
controllerId.apply {
|
||||||
|
when (ev) {
|
||||||
|
is Event.Button -> {
|
||||||
|
// Ignore legacy L3/R3 via stick press (double tap)
|
||||||
|
if (ev.id == DUMMY_LEFT_STICK_PRESS_ID || ev.id == DUMMY_RIGHT_STICK_PRESS_ID) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
when (ev.action) {
|
||||||
|
KeyEvent.ACTION_UP -> KenjinxNative.inputSetButtonReleased(ev.id, this)
|
||||||
|
KeyEvent.ACTION_DOWN -> KenjinxNative.inputSetButtonPressed(ev.id, this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
is Event.Direction -> {
|
||||||
|
when (ev.id) {
|
||||||
|
GamePadButtonInputId.DpadUp.ordinal -> {
|
||||||
|
// Horizontal
|
||||||
|
if (ev.xAxis > 0) {
|
||||||
|
KenjinxNative.inputSetButtonPressed(GamePadButtonInputId.DpadRight.ordinal, this)
|
||||||
|
KenjinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadLeft.ordinal, this)
|
||||||
|
} else if (ev.xAxis < 0) {
|
||||||
|
KenjinxNative.inputSetButtonPressed(GamePadButtonInputId.DpadLeft.ordinal, this)
|
||||||
|
KenjinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadRight.ordinal, this)
|
||||||
|
} else {
|
||||||
|
KenjinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadLeft.ordinal, this)
|
||||||
|
KenjinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadRight.ordinal, this)
|
||||||
|
}
|
||||||
|
// Vertical
|
||||||
|
if (ev.yAxis < 0) {
|
||||||
|
KenjinxNative.inputSetButtonPressed(GamePadButtonInputId.DpadUp.ordinal, this)
|
||||||
|
KenjinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadDown.ordinal, this)
|
||||||
|
} else if (ev.yAxis > 0) {
|
||||||
|
KenjinxNative.inputSetButtonPressed(GamePadButtonInputId.DpadDown.ordinal, this)
|
||||||
|
KenjinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadUp.ordinal, this)
|
||||||
|
} else {
|
||||||
|
KenjinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadDown.ordinal, this)
|
||||||
|
KenjinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadUp.ordinal, this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GamePadButtonInputId.LeftStick.ordinal -> {
|
||||||
|
val setting = QuickSettings(activity)
|
||||||
|
val x = MathUtils.clamp(ev.xAxis * setting.controllerStickSensitivity, -1f, 1f)
|
||||||
|
val y = MathUtils.clamp(ev.yAxis * setting.controllerStickSensitivity, -1f, 1f)
|
||||||
|
KenjinxNative.inputSetStickAxis(1, x, -y, this)
|
||||||
|
}
|
||||||
|
|
||||||
|
GamePadButtonInputId.RightStick.ordinal -> {
|
||||||
|
val setting = QuickSettings(activity)
|
||||||
|
val x = MathUtils.clamp(ev.xAxis * setting.controllerStickSensitivity, -1f, 1f)
|
||||||
|
val y = MathUtils.clamp(ev.yAxis * setting.controllerStickSensitivity, -1f, 1f)
|
||||||
|
KenjinxNative.inputSetStickAxis(2, x, -y, this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun <T> Flow<T>.safeCollect(block: suspend (T) -> Unit) {
|
||||||
|
this.catch { }
|
||||||
|
.collect { block(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun generateConfig(isLeft: Boolean, useSwitchLayout: Boolean): GamePadConfig {
|
||||||
|
val distance = 0.3f
|
||||||
|
val buttonScale = 1f
|
||||||
|
|
||||||
|
if (isLeft) {
|
||||||
|
return GamePadConfig(
|
||||||
|
/* ringSegments = */ 12,
|
||||||
|
/* Primary (Stick) */
|
||||||
|
// IMPORTANT: pressButtonId -> DUMMY_LEFT_STICK_PRESS_ID, so that double tap does not trigger L3
|
||||||
|
PrimaryDialConfig.Stick(
|
||||||
|
GamePadButtonInputId.LeftStick.ordinal,
|
||||||
|
DUMMY_LEFT_STICK_PRESS_ID,
|
||||||
|
setOf(),
|
||||||
|
"LeftStick",
|
||||||
|
null
|
||||||
|
),
|
||||||
|
listOf(
|
||||||
|
// D-Pad
|
||||||
|
SecondaryDialConfig.Cross(
|
||||||
|
/* sector */ 10,
|
||||||
|
/* size */ 3,
|
||||||
|
/* gap */ 2.5f,
|
||||||
|
distance,
|
||||||
|
CrossConfig(
|
||||||
|
GamePadButtonInputId.DpadUp.ordinal,
|
||||||
|
CrossConfig.Shape.STANDARD,
|
||||||
|
null,
|
||||||
|
setOf(),
|
||||||
|
CrossContentDescription(),
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
SecondaryDialConfig.RotationProcessor()
|
||||||
|
),
|
||||||
|
|
||||||
|
// Minus
|
||||||
|
SecondaryDialConfig.SingleButton(
|
||||||
|
/* sector */ 1,
|
||||||
|
buttonScale,
|
||||||
|
distance,
|
||||||
|
ButtonConfig(
|
||||||
|
GamePadButtonInputId.Minus.ordinal,
|
||||||
|
"-",
|
||||||
|
true,
|
||||||
|
null,
|
||||||
|
"Minus",
|
||||||
|
setOf(),
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
SecondaryDialConfig.RotationProcessor()
|
||||||
|
),
|
||||||
|
|
||||||
|
// L Bumper
|
||||||
|
SecondaryDialConfig.DoubleButton(
|
||||||
|
/* sector */ 2,
|
||||||
|
distance,
|
||||||
|
ButtonConfig(
|
||||||
|
GamePadButtonInputId.LeftShoulder.ordinal,
|
||||||
|
"L",
|
||||||
|
true,
|
||||||
|
null,
|
||||||
|
"LeftBumper",
|
||||||
|
setOf(),
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
SecondaryDialConfig.RotationProcessor()
|
||||||
|
),
|
||||||
|
|
||||||
|
// ZL Trigger
|
||||||
|
SecondaryDialConfig.SingleButton(
|
||||||
|
/* sector */ 9,
|
||||||
|
buttonScale,
|
||||||
|
distance,
|
||||||
|
ButtonConfig(
|
||||||
|
GamePadButtonInputId.LeftTrigger.ordinal,
|
||||||
|
"ZL",
|
||||||
|
true,
|
||||||
|
null,
|
||||||
|
"LeftTrigger",
|
||||||
|
setOf(),
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
SecondaryDialConfig.RotationProcessor()
|
||||||
|
),
|
||||||
|
|
||||||
|
// L3 as a separate button
|
||||||
|
SecondaryDialConfig.SingleButton(
|
||||||
|
/* sector */ 1,
|
||||||
|
buttonScale,
|
||||||
|
1.5f,
|
||||||
|
ButtonConfig(
|
||||||
|
GamePadButtonInputId.LeftStickButton.ordinal,
|
||||||
|
"L3",
|
||||||
|
true,
|
||||||
|
null,
|
||||||
|
"LeftStickButton",
|
||||||
|
setOf(),
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
SecondaryDialConfig.RotationProcessor()
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return GamePadConfig(
|
||||||
|
/* ringSegments = */ 12,
|
||||||
|
/* Primary (ABXY) */
|
||||||
|
PrimaryDialConfig.PrimaryButtons(
|
||||||
|
if (useSwitchLayout) {
|
||||||
|
// Switch/Nintendo: A=Right, X=Top, Y=Left, B=Bottom
|
||||||
|
listOf(
|
||||||
|
ButtonConfig(GamePadButtonInputId.A.ordinal, "A", true, null, "A", setOf(), true, null), // Right
|
||||||
|
ButtonConfig(GamePadButtonInputId.X.ordinal, "X", true, null, "X", setOf(), true, null), // Top
|
||||||
|
ButtonConfig(GamePadButtonInputId.Y.ordinal, "Y", true, null, "Y", setOf(), true, null), // Left
|
||||||
|
ButtonConfig(GamePadButtonInputId.B.ordinal, "B", true, null, "B", setOf(), true, null) // Bottom
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
// Xbox Style: B=Right, Y=Top, X=Left, A=Bottom
|
||||||
|
listOf(
|
||||||
|
ButtonConfig(GamePadButtonInputId.B.ordinal, "B", true, null, "B", setOf(), true, null), // Right
|
||||||
|
ButtonConfig(GamePadButtonInputId.Y.ordinal, "Y", true, null, "Y", setOf(), true, null), // Top
|
||||||
|
ButtonConfig(GamePadButtonInputId.X.ordinal, "X", true, null, "X", setOf(), true, null), // Left
|
||||||
|
ButtonConfig(GamePadButtonInputId.A.ordinal, "A", true, null, "A", setOf(), true, null) // Bottom
|
||||||
|
)
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
0f,
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
listOf(
|
||||||
|
// Right stick (unchanged)
|
||||||
|
// IMPORTANT: pressButtonId -> DUMMY_RIGHT_STICK_PRESS_ID, so that double tap does not trigger R3
|
||||||
|
SecondaryDialConfig.Stick(
|
||||||
|
/* sector */ 7,
|
||||||
|
/* size */ 2,
|
||||||
|
/* gap */ 2f,
|
||||||
|
distance,
|
||||||
|
GamePadButtonInputId.RightStick.ordinal,
|
||||||
|
DUMMY_RIGHT_STICK_PRESS_ID,
|
||||||
|
null,
|
||||||
|
setOf(),
|
||||||
|
"RightStick",
|
||||||
|
SecondaryDialConfig.RotationProcessor()
|
||||||
|
),
|
||||||
|
|
||||||
|
// Plus
|
||||||
|
SecondaryDialConfig.SingleButton(
|
||||||
|
/* sector */ 6,
|
||||||
|
buttonScale,
|
||||||
|
distance,
|
||||||
|
ButtonConfig(
|
||||||
|
GamePadButtonInputId.Plus.ordinal,
|
||||||
|
"+",
|
||||||
|
true,
|
||||||
|
null,
|
||||||
|
"Plus",
|
||||||
|
setOf(),
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
SecondaryDialConfig.RotationProcessor()
|
||||||
|
),
|
||||||
|
|
||||||
|
// R Bumper
|
||||||
|
SecondaryDialConfig.DoubleButton(
|
||||||
|
/* sector */ 3,
|
||||||
|
distance,
|
||||||
|
ButtonConfig(
|
||||||
|
GamePadButtonInputId.RightShoulder.ordinal,
|
||||||
|
"R",
|
||||||
|
true,
|
||||||
|
null,
|
||||||
|
"RightBumper",
|
||||||
|
setOf(),
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
SecondaryDialConfig.RotationProcessor()
|
||||||
|
),
|
||||||
|
|
||||||
|
// ZR Trigger
|
||||||
|
SecondaryDialConfig.SingleButton(
|
||||||
|
/* sector */ 9,
|
||||||
|
buttonScale,
|
||||||
|
distance,
|
||||||
|
ButtonConfig(
|
||||||
|
GamePadButtonInputId.RightTrigger.ordinal,
|
||||||
|
"ZR",
|
||||||
|
true,
|
||||||
|
null,
|
||||||
|
"RightTrigger",
|
||||||
|
setOf(),
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
SecondaryDialConfig.RotationProcessor()
|
||||||
|
),
|
||||||
|
|
||||||
|
// R3 as a separate button
|
||||||
|
SecondaryDialConfig.SingleButton(
|
||||||
|
/* sector */ 5,
|
||||||
|
buttonScale,
|
||||||
|
1.5f,
|
||||||
|
ButtonConfig(
|
||||||
|
GamePadButtonInputId.RightStickButton.ordinal,
|
||||||
|
"R3",
|
||||||
|
true,
|
||||||
|
null,
|
||||||
|
"RightStickButton",
|
||||||
|
setOf(),
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
SecondaryDialConfig.RotationProcessor()
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,420 @@
|
|||||||
|
package org.kenjinx.android
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Context
|
||||||
|
import android.view.KeyEvent
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.FrameLayout
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.viewinterop.AndroidView
|
||||||
|
import androidx.core.math.MathUtils
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import com.swordfish.radialgamepad.library.RadialGamePad
|
||||||
|
import com.swordfish.radialgamepad.library.config.ButtonConfig
|
||||||
|
import com.swordfish.radialgamepad.library.config.CrossConfig
|
||||||
|
import com.swordfish.radialgamepad.library.config.CrossContentDescription
|
||||||
|
import com.swordfish.radialgamepad.library.config.PrimaryDialConfig
|
||||||
|
import com.swordfish.radialgamepad.library.config.SecondaryDialConfig
|
||||||
|
import com.swordfish.radialgamepad.library.event.Event
|
||||||
|
import kotlinx.coroutines.flow.merge
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import org.kenjinx.android.viewmodels.MainViewModel
|
||||||
|
import org.kenjinx.android.viewmodels.QuickSettings
|
||||||
|
|
||||||
|
// --- Dummy IDs to disconnect legacy L3/R3 (stick double tap/tap) ---
|
||||||
|
private const val DUMMY_LEFT_STICK_PRESS_ID = 10001
|
||||||
|
private const val DUMMY_RIGHT_STICK_PRESS_ID = 10002
|
||||||
|
|
||||||
|
class GameController2(var activity: Activity) : IGameController {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private fun init(context: Context, controller: GameController2): View {
|
||||||
|
val inflater = LayoutInflater.from(context)
|
||||||
|
val parent = FrameLayout(context)
|
||||||
|
val view = inflater.inflate(R.layout.game_layout, parent, false)
|
||||||
|
|
||||||
|
val leftContainer = view.findViewById<FrameLayout>(R.id.leftcontainer)!!
|
||||||
|
val rightContainer = view.findViewById<FrameLayout>(R.id.rightcontainer)!!
|
||||||
|
|
||||||
|
leftContainer.addView(controller.leftGamePad)
|
||||||
|
rightContainer.addView(controller.rightGamePad)
|
||||||
|
|
||||||
|
leftContainer.addOnLayoutChangeListener { v, _, _, _, _, _, _, _, _ ->
|
||||||
|
val scale = QuickSettings(controller.activity).controllerScale.coerceIn(0.5f, 1.5f)
|
||||||
|
v.pivotX = 0f
|
||||||
|
v.pivotY = v.height.toFloat()
|
||||||
|
v.scaleX = scale
|
||||||
|
v.scaleY = scale
|
||||||
|
}
|
||||||
|
rightContainer.addOnLayoutChangeListener { v, _, _, _, _, _, _, _, _ ->
|
||||||
|
val scale = QuickSettings(controller.activity).controllerScale.coerceIn(0.5f, 1.5f)
|
||||||
|
v.pivotX = v.width.toFloat()
|
||||||
|
v.pivotY = v.height.toFloat()
|
||||||
|
v.scaleX = scale
|
||||||
|
v.scaleY = scale
|
||||||
|
}
|
||||||
|
|
||||||
|
leftContainer.post { leftContainer.requestLayout() }
|
||||||
|
rightContainer.post { rightContainer.requestLayout() }
|
||||||
|
|
||||||
|
return view
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun Compose(viewModel: MainViewModel) {
|
||||||
|
AndroidView(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
factory = { context ->
|
||||||
|
val controller = GameController2(viewModel.activity)
|
||||||
|
val c = init(context, controller)
|
||||||
|
|
||||||
|
viewModel.activity.lifecycleScope.launch {
|
||||||
|
val events = merge(
|
||||||
|
controller.leftGamePad.events(),
|
||||||
|
controller.rightGamePad.events()
|
||||||
|
)
|
||||||
|
events.safeCollect { controller.handleEvent(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
controller.controllerView = c
|
||||||
|
viewModel.setGameController(controller)
|
||||||
|
controller.setVisible(QuickSettings(viewModel.activity).useVirtualController)
|
||||||
|
c
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private var controllerView: View? = null
|
||||||
|
var leftGamePad: RadialGamePad
|
||||||
|
var rightGamePad: RadialGamePad
|
||||||
|
var controllerId: Int = -1
|
||||||
|
override val isVisible: Boolean
|
||||||
|
get() = controllerView?.isVisible ?: false
|
||||||
|
|
||||||
|
init {
|
||||||
|
val useSwitchLayout = QuickSettings(activity).useSwitchLayout
|
||||||
|
leftGamePad = RadialGamePad(generateConfig2(true, useSwitchLayout), 16f, activity)
|
||||||
|
rightGamePad = RadialGamePad(generateConfig2(false, useSwitchLayout), 16f, activity)
|
||||||
|
|
||||||
|
leftGamePad.primaryDialMaxSizeDp = 200f
|
||||||
|
rightGamePad.primaryDialMaxSizeDp = 200f
|
||||||
|
|
||||||
|
leftGamePad.gravityX = -1f
|
||||||
|
leftGamePad.gravityY = 1f
|
||||||
|
rightGamePad.gravityX = 1f
|
||||||
|
rightGamePad.gravityY = 1f
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setVisible(isVisible: Boolean) {
|
||||||
|
controllerView?.apply {
|
||||||
|
this.isVisible = isVisible
|
||||||
|
if (isVisible) connect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun connect() {
|
||||||
|
if (controllerId == -1)
|
||||||
|
controllerId = KenjinxNative.inputConnectGamepad(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleEvent(ev: Event) {
|
||||||
|
if (controllerId == -1)
|
||||||
|
controllerId = KenjinxNative.inputConnectGamepad(0)
|
||||||
|
|
||||||
|
controllerId.apply {
|
||||||
|
when (ev) {
|
||||||
|
is Event.Button -> {
|
||||||
|
// Ignore legacy L3/R3 via stick press (double tap)
|
||||||
|
if (ev.id == DUMMY_LEFT_STICK_PRESS_ID || ev.id == DUMMY_RIGHT_STICK_PRESS_ID) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
when (ev.action) {
|
||||||
|
KeyEvent.ACTION_UP -> KenjinxNative.inputSetButtonReleased(ev.id, this)
|
||||||
|
KeyEvent.ACTION_DOWN -> KenjinxNative.inputSetButtonPressed(ev.id, this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
is Event.Direction -> {
|
||||||
|
when (ev.id) {
|
||||||
|
GamePadButtonInputId.DpadUp.ordinal -> {
|
||||||
|
// Horizontal
|
||||||
|
if (ev.xAxis > 0) {
|
||||||
|
KenjinxNative.inputSetButtonPressed(GamePadButtonInputId.DpadRight.ordinal, this)
|
||||||
|
KenjinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadLeft.ordinal, this)
|
||||||
|
} else if (ev.xAxis < 0) {
|
||||||
|
KenjinxNative.inputSetButtonPressed(GamePadButtonInputId.DpadLeft.ordinal, this)
|
||||||
|
KenjinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadRight.ordinal, this)
|
||||||
|
} else {
|
||||||
|
KenjinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadLeft.ordinal, this)
|
||||||
|
KenjinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadRight.ordinal, this)
|
||||||
|
}
|
||||||
|
// Vertical
|
||||||
|
if (ev.yAxis < 0) {
|
||||||
|
KenjinxNative.inputSetButtonPressed(GamePadButtonInputId.DpadUp.ordinal, this)
|
||||||
|
KenjinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadDown.ordinal, this)
|
||||||
|
} else if (ev.yAxis > 0) {
|
||||||
|
KenjinxNative.inputSetButtonPressed(GamePadButtonInputId.DpadDown.ordinal, this)
|
||||||
|
KenjinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadUp.ordinal, this)
|
||||||
|
} else {
|
||||||
|
KenjinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadDown.ordinal, this)
|
||||||
|
KenjinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadUp.ordinal, this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GamePadButtonInputId.LeftStick.ordinal -> {
|
||||||
|
val setting = QuickSettings(activity)
|
||||||
|
val x = MathUtils.clamp(ev.xAxis * setting.controllerStickSensitivity, -1f, 1f)
|
||||||
|
val y = MathUtils.clamp(ev.yAxis * setting.controllerStickSensitivity, -1f, 1f)
|
||||||
|
KenjinxNative.inputSetStickAxis(1, x, -y, this)
|
||||||
|
}
|
||||||
|
|
||||||
|
GamePadButtonInputId.RightStick.ordinal -> {
|
||||||
|
val setting = QuickSettings(activity)
|
||||||
|
val x = MathUtils.clamp(ev.xAxis * setting.controllerStickSensitivity, -1f, 1f)
|
||||||
|
val y = MathUtils.clamp(ev.yAxis * setting.controllerStickSensitivity, -1f, 1f)
|
||||||
|
KenjinxNative.inputSetStickAxis(2, x, -y, this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Local copy of the config generation (name = generateConfig2, to avoid conflicts) ---
|
||||||
|
private fun generateConfig2(isLeft: Boolean, useSwitchLayout: Boolean): GamePadConfig {
|
||||||
|
val distance = 0.3f
|
||||||
|
val buttonScale = 1f
|
||||||
|
|
||||||
|
if (isLeft) {
|
||||||
|
return GamePadConfig(
|
||||||
|
/* ringSegments = */ 12,
|
||||||
|
/* Primary (Stick) */
|
||||||
|
// IMPORTANT: pressButtonId -> DUMMY_LEFT_STICK_PRESS_ID, so that double tap does not trigger L3
|
||||||
|
PrimaryDialConfig.Stick(
|
||||||
|
GamePadButtonInputId.LeftStick.ordinal,
|
||||||
|
DUMMY_LEFT_STICK_PRESS_ID,
|
||||||
|
setOf(),
|
||||||
|
"LeftStick",
|
||||||
|
null
|
||||||
|
),
|
||||||
|
listOf(
|
||||||
|
// D-Pad
|
||||||
|
SecondaryDialConfig.Cross(
|
||||||
|
/* sector */ 10,
|
||||||
|
/* size */ 3,
|
||||||
|
/* gap */ 2.1f,
|
||||||
|
distance,
|
||||||
|
CrossConfig(
|
||||||
|
GamePadButtonInputId.DpadUp.ordinal,
|
||||||
|
CrossConfig.Shape.STANDARD,
|
||||||
|
null,
|
||||||
|
setOf(),
|
||||||
|
CrossContentDescription(),
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
SecondaryDialConfig.RotationProcessor()
|
||||||
|
),
|
||||||
|
|
||||||
|
// Minus
|
||||||
|
SecondaryDialConfig.SingleButton(
|
||||||
|
/* sector */ 9,
|
||||||
|
buttonScale,
|
||||||
|
0.3f,
|
||||||
|
ButtonConfig(
|
||||||
|
GamePadButtonInputId.Minus.ordinal,
|
||||||
|
"-",
|
||||||
|
true,
|
||||||
|
null,
|
||||||
|
"Minus",
|
||||||
|
setOf(),
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
SecondaryDialConfig.RotationProcessor()
|
||||||
|
),
|
||||||
|
|
||||||
|
// L Bumper
|
||||||
|
SecondaryDialConfig.DoubleButton(
|
||||||
|
/* sector */ 2,
|
||||||
|
0.2f,
|
||||||
|
ButtonConfig(
|
||||||
|
GamePadButtonInputId.LeftShoulder.ordinal,
|
||||||
|
"L",
|
||||||
|
true,
|
||||||
|
null,
|
||||||
|
"LeftBumper",
|
||||||
|
setOf(),
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
SecondaryDialConfig.RotationProcessor()
|
||||||
|
),
|
||||||
|
|
||||||
|
// ZL Trigger
|
||||||
|
SecondaryDialConfig.DoubleButton(
|
||||||
|
/* sector */ 2,
|
||||||
|
1f,
|
||||||
|
ButtonConfig(
|
||||||
|
GamePadButtonInputId.LeftTrigger.ordinal,
|
||||||
|
"ZL",
|
||||||
|
true,
|
||||||
|
null,
|
||||||
|
"LeftTrigger",
|
||||||
|
setOf(),
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
SecondaryDialConfig.RotationProcessor()
|
||||||
|
),
|
||||||
|
|
||||||
|
// L3 as a separate button
|
||||||
|
SecondaryDialConfig.SingleButton(
|
||||||
|
/* sector */ 1,
|
||||||
|
buttonScale,
|
||||||
|
1.0f,
|
||||||
|
ButtonConfig(
|
||||||
|
GamePadButtonInputId.LeftStickButton.ordinal,
|
||||||
|
"L3",
|
||||||
|
true,
|
||||||
|
null,
|
||||||
|
"LeftStickButton",
|
||||||
|
setOf(),
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
SecondaryDialConfig.RotationProcessor()
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return GamePadConfig(
|
||||||
|
/* ringSegments = */ 12,
|
||||||
|
/* Primary (ABXY) */
|
||||||
|
PrimaryDialConfig.PrimaryButtons(
|
||||||
|
if (useSwitchLayout) {
|
||||||
|
// Switch/Nintendo: A=Right, X=Top, Y=Left, B=Bottom
|
||||||
|
listOf(
|
||||||
|
ButtonConfig(GamePadButtonInputId.A.ordinal, "A", true, null, "A", setOf(), true, null), // Right
|
||||||
|
ButtonConfig(GamePadButtonInputId.X.ordinal, "X", true, null, "X", setOf(), true, null), // Top
|
||||||
|
ButtonConfig(GamePadButtonInputId.Y.ordinal, "Y", true, null, "Y", setOf(), true, null), // Left
|
||||||
|
ButtonConfig(GamePadButtonInputId.B.ordinal, "B", true, null, "B", setOf(), true, null) // Bottom
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
// Xbox Style: B=Right, Y=Top, X=Left, A=Bottom
|
||||||
|
listOf(
|
||||||
|
ButtonConfig(GamePadButtonInputId.B.ordinal, "B", true, null, "B", setOf(), true, null), // Right
|
||||||
|
ButtonConfig(GamePadButtonInputId.Y.ordinal, "Y", true, null, "Y", setOf(), true, null), // Top
|
||||||
|
ButtonConfig(GamePadButtonInputId.X.ordinal, "X", true, null, "X", setOf(), true, null), // Left
|
||||||
|
ButtonConfig(GamePadButtonInputId.A.ordinal, "A", true, null, "A", setOf(), true, null) // Bottom
|
||||||
|
)
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
0f,
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
listOf(
|
||||||
|
// Right stick
|
||||||
|
// IMPORTANT: pressButtonId -> DUMMY_RIGHT_STICK_PRESS_ID, so that double tap does not trigger R3
|
||||||
|
SecondaryDialConfig.Stick(
|
||||||
|
/* sector */ 6,
|
||||||
|
/* size */ 3,
|
||||||
|
/* gap */ 2.7f,
|
||||||
|
distance,
|
||||||
|
GamePadButtonInputId.RightStick.ordinal,
|
||||||
|
DUMMY_RIGHT_STICK_PRESS_ID,
|
||||||
|
null,
|
||||||
|
setOf(),
|
||||||
|
"RightStick",
|
||||||
|
SecondaryDialConfig.RotationProcessor()
|
||||||
|
),
|
||||||
|
|
||||||
|
// Plus
|
||||||
|
SecondaryDialConfig.SingleButton(
|
||||||
|
/* sector */ 9,
|
||||||
|
buttonScale,
|
||||||
|
0.3f,
|
||||||
|
ButtonConfig(
|
||||||
|
GamePadButtonInputId.Plus.ordinal,
|
||||||
|
"+",
|
||||||
|
true,
|
||||||
|
null,
|
||||||
|
"Plus",
|
||||||
|
setOf(),
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
SecondaryDialConfig.RotationProcessor()
|
||||||
|
),
|
||||||
|
|
||||||
|
// R Bumper
|
||||||
|
SecondaryDialConfig.DoubleButton(
|
||||||
|
/* sector */ 3,
|
||||||
|
0.2f,
|
||||||
|
ButtonConfig(
|
||||||
|
GamePadButtonInputId.RightShoulder.ordinal,
|
||||||
|
"R",
|
||||||
|
true,
|
||||||
|
null,
|
||||||
|
"RightBumper",
|
||||||
|
setOf(),
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
SecondaryDialConfig.RotationProcessor()
|
||||||
|
),
|
||||||
|
|
||||||
|
// ZR Trigger
|
||||||
|
SecondaryDialConfig.DoubleButton(
|
||||||
|
/* sector */ 3,
|
||||||
|
1f,
|
||||||
|
ButtonConfig(
|
||||||
|
GamePadButtonInputId.RightTrigger.ordinal,
|
||||||
|
"ZR",
|
||||||
|
true,
|
||||||
|
null,
|
||||||
|
"RightTrigger",
|
||||||
|
setOf(),
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
SecondaryDialConfig.RotationProcessor()
|
||||||
|
),
|
||||||
|
|
||||||
|
// R3 as a separate button
|
||||||
|
SecondaryDialConfig.SingleButton(
|
||||||
|
/* sector */ 5,
|
||||||
|
buttonScale,
|
||||||
|
1.0f,
|
||||||
|
ButtonConfig(
|
||||||
|
GamePadButtonInputId.RightStickButton.ordinal,
|
||||||
|
"R3",
|
||||||
|
true,
|
||||||
|
null,
|
||||||
|
"RightStickButton",
|
||||||
|
setOf(),
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
SecondaryDialConfig.RotationProcessor()
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,413 @@
|
|||||||
|
package org.kenjinx.android
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Context
|
||||||
|
import android.view.KeyEvent
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.FrameLayout
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.viewinterop.AndroidView
|
||||||
|
import androidx.core.math.MathUtils
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import com.swordfish.radialgamepad.library.RadialGamePad
|
||||||
|
import com.swordfish.radialgamepad.library.config.ButtonConfig
|
||||||
|
import com.swordfish.radialgamepad.library.config.CrossConfig
|
||||||
|
import com.swordfish.radialgamepad.library.config.CrossContentDescription
|
||||||
|
import com.swordfish.radialgamepad.library.config.PrimaryDialConfig
|
||||||
|
import com.swordfish.radialgamepad.library.config.SecondaryDialConfig
|
||||||
|
import com.swordfish.radialgamepad.library.event.Event
|
||||||
|
import kotlinx.coroutines.flow.merge
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import org.kenjinx.android.viewmodels.MainViewModel
|
||||||
|
import org.kenjinx.android.viewmodels.QuickSettings
|
||||||
|
|
||||||
|
// --- Dummy IDs to disconnect legacy L3/R3 (stick double tap/tap) ---
|
||||||
|
private const val DUMMY_LEFT_STICK_PRESS_ID = 10001
|
||||||
|
private const val DUMMY_RIGHT_STICK_PRESS_ID = 10002
|
||||||
|
|
||||||
|
class GameController3(var activity: Activity) : IGameController {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private fun init(context: Context, controller: GameController3): View {
|
||||||
|
val inflater = LayoutInflater.from(context)
|
||||||
|
val parent = FrameLayout(context)
|
||||||
|
val view = inflater.inflate(R.layout.game_layout, parent, false)
|
||||||
|
|
||||||
|
val leftContainer = view.findViewById<FrameLayout>(R.id.leftcontainer)!!
|
||||||
|
val rightContainer = view.findViewById<FrameLayout>(R.id.rightcontainer)!!
|
||||||
|
|
||||||
|
leftContainer.addView(controller.leftGamePad)
|
||||||
|
rightContainer.addView(controller.rightGamePad)
|
||||||
|
|
||||||
|
leftContainer.addOnLayoutChangeListener { v, _, _, _, _, _, _, _, _ ->
|
||||||
|
val scale = QuickSettings(controller.activity).controllerScale.coerceIn(0.5f, 1.5f)
|
||||||
|
v.pivotX = 0f
|
||||||
|
v.pivotY = v.height.toFloat()
|
||||||
|
v.scaleX = scale
|
||||||
|
v.scaleY = scale
|
||||||
|
}
|
||||||
|
rightContainer.addOnLayoutChangeListener { v, _, _, _, _, _, _, _, _ ->
|
||||||
|
val scale = QuickSettings(controller.activity).controllerScale.coerceIn(0.5f, 1.5f)
|
||||||
|
v.pivotX = v.width.toFloat()
|
||||||
|
v.pivotY = v.height.toFloat()
|
||||||
|
v.scaleX = scale
|
||||||
|
v.scaleY = scale
|
||||||
|
}
|
||||||
|
|
||||||
|
leftContainer.post { leftContainer.requestLayout() }
|
||||||
|
rightContainer.post { rightContainer.requestLayout() }
|
||||||
|
|
||||||
|
return view
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun Compose(viewModel: MainViewModel) {
|
||||||
|
AndroidView(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
factory = { context ->
|
||||||
|
val controller = GameController3(viewModel.activity)
|
||||||
|
val c = init(context, controller)
|
||||||
|
|
||||||
|
viewModel.activity.lifecycleScope.launch {
|
||||||
|
val events = merge(
|
||||||
|
controller.leftGamePad.events(),
|
||||||
|
controller.rightGamePad.events()
|
||||||
|
)
|
||||||
|
events.safeCollect { controller.handleEvent(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
controller.controllerView = c
|
||||||
|
viewModel.setGameController(controller)
|
||||||
|
controller.setVisible(QuickSettings(viewModel.activity).useVirtualController)
|
||||||
|
c
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var controllerView: View? = null
|
||||||
|
var leftGamePad: RadialGamePad
|
||||||
|
var rightGamePad: RadialGamePad
|
||||||
|
var controllerId: Int = -1
|
||||||
|
override val isVisible: Boolean
|
||||||
|
get() = controllerView?.isVisible ?: false
|
||||||
|
|
||||||
|
init {
|
||||||
|
val useSwitchLayout = QuickSettings(activity).useSwitchLayout
|
||||||
|
leftGamePad = RadialGamePad(generateConfig3(true, useSwitchLayout), 16f, activity)
|
||||||
|
rightGamePad = RadialGamePad(generateConfig3(false, useSwitchLayout), 16f, activity)
|
||||||
|
|
||||||
|
leftGamePad.primaryDialMaxSizeDp = 200f
|
||||||
|
rightGamePad.primaryDialMaxSizeDp = 200f
|
||||||
|
|
||||||
|
leftGamePad.gravityX = -1f
|
||||||
|
leftGamePad.gravityY = 1f
|
||||||
|
rightGamePad.gravityX = 1f
|
||||||
|
rightGamePad.gravityY = 1f
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setVisible(isVisible: Boolean) {
|
||||||
|
controllerView?.apply {
|
||||||
|
this.isVisible = isVisible
|
||||||
|
if (isVisible) connect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun connect() {
|
||||||
|
if (controllerId == -1)
|
||||||
|
controllerId = KenjinxNative.inputConnectGamepad(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleEvent(ev: Event) {
|
||||||
|
if (controllerId == -1)
|
||||||
|
controllerId = KenjinxNative.inputConnectGamepad(0)
|
||||||
|
|
||||||
|
controllerId.apply {
|
||||||
|
when (ev) {
|
||||||
|
is Event.Button -> {
|
||||||
|
// Ignore legacy L3/R3 via stick press (double tap)
|
||||||
|
if (ev.id == DUMMY_LEFT_STICK_PRESS_ID || ev.id == DUMMY_RIGHT_STICK_PRESS_ID) return
|
||||||
|
when (ev.action) {
|
||||||
|
KeyEvent.ACTION_UP -> KenjinxNative.inputSetButtonReleased(ev.id, this)
|
||||||
|
KeyEvent.ACTION_DOWN -> KenjinxNative.inputSetButtonPressed(ev.id, this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is Event.Direction -> {
|
||||||
|
when (ev.id) {
|
||||||
|
GamePadButtonInputId.DpadUp.ordinal -> {
|
||||||
|
// Horizontal
|
||||||
|
if (ev.xAxis > 0) {
|
||||||
|
KenjinxNative.inputSetButtonPressed(GamePadButtonInputId.DpadRight.ordinal, this)
|
||||||
|
KenjinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadLeft.ordinal, this)
|
||||||
|
} else if (ev.xAxis < 0) {
|
||||||
|
KenjinxNative.inputSetButtonPressed(GamePadButtonInputId.DpadLeft.ordinal, this)
|
||||||
|
KenjinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadRight.ordinal, this)
|
||||||
|
} else {
|
||||||
|
KenjinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadLeft.ordinal, this)
|
||||||
|
KenjinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadRight.ordinal, this)
|
||||||
|
}
|
||||||
|
// Vertical
|
||||||
|
if (ev.yAxis < 0) {
|
||||||
|
KenjinxNative.inputSetButtonPressed(GamePadButtonInputId.DpadUp.ordinal, this)
|
||||||
|
KenjinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadDown.ordinal, this)
|
||||||
|
} else if (ev.yAxis > 0) {
|
||||||
|
KenjinxNative.inputSetButtonPressed(GamePadButtonInputId.DpadDown.ordinal, this)
|
||||||
|
KenjinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadUp.ordinal, this)
|
||||||
|
} else {
|
||||||
|
KenjinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadDown.ordinal, this)
|
||||||
|
KenjinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadUp.ordinal, this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GamePadButtonInputId.LeftStick.ordinal -> {
|
||||||
|
val setting = QuickSettings(activity)
|
||||||
|
val x = MathUtils.clamp(ev.xAxis * setting.controllerStickSensitivity, -1f, 1f)
|
||||||
|
val y = MathUtils.clamp(ev.yAxis * setting.controllerStickSensitivity, -1f, 1f)
|
||||||
|
KenjinxNative.inputSetStickAxis(1, x, -y, this)
|
||||||
|
}
|
||||||
|
GamePadButtonInputId.RightStick.ordinal -> {
|
||||||
|
val setting = QuickSettings(activity)
|
||||||
|
val x = MathUtils.clamp(ev.xAxis * setting.controllerStickSensitivity, -1f, 1f)
|
||||||
|
val y = MathUtils.clamp(ev.yAxis * setting.controllerStickSensitivity, -1f, 1f)
|
||||||
|
KenjinxNative.inputSetStickAxis(2, x, -y, this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun generateConfig3(isLeft: Boolean, useSwitchLayout: Boolean): GamePadConfig {
|
||||||
|
val distance = 0.3f
|
||||||
|
val buttonScale = 1f
|
||||||
|
|
||||||
|
if (isLeft) {
|
||||||
|
return GamePadConfig(
|
||||||
|
/* ringSegments = */ 12,
|
||||||
|
/* Primary (D-Pad) */
|
||||||
|
PrimaryDialConfig.Cross(
|
||||||
|
CrossConfig(
|
||||||
|
GamePadButtonInputId.DpadUp.ordinal,
|
||||||
|
CrossConfig.Shape.STANDARD,
|
||||||
|
null,
|
||||||
|
setOf(),
|
||||||
|
CrossContentDescription(),
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
),
|
||||||
|
listOf(
|
||||||
|
// Left stick
|
||||||
|
// IMPORTANT: pressButtonId -> DUMMY_LEFT_STICK_PRESS_ID, so that double tap does not trigger L3
|
||||||
|
SecondaryDialConfig.Stick(
|
||||||
|
/* sector */ 10,
|
||||||
|
/* size */ 3,
|
||||||
|
/* gap */ 2.7f,
|
||||||
|
distance,
|
||||||
|
GamePadButtonInputId.LeftStick.ordinal,
|
||||||
|
DUMMY_LEFT_STICK_PRESS_ID,
|
||||||
|
null,
|
||||||
|
setOf(),
|
||||||
|
"LeftStick",
|
||||||
|
SecondaryDialConfig.RotationProcessor()
|
||||||
|
),
|
||||||
|
|
||||||
|
// Minus
|
||||||
|
SecondaryDialConfig.SingleButton(
|
||||||
|
/* sector */ 9,
|
||||||
|
buttonScale,
|
||||||
|
0.3f,
|
||||||
|
ButtonConfig(
|
||||||
|
GamePadButtonInputId.Minus.ordinal,
|
||||||
|
"-",
|
||||||
|
true,
|
||||||
|
null,
|
||||||
|
"Minus",
|
||||||
|
setOf(),
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
SecondaryDialConfig.RotationProcessor()
|
||||||
|
),
|
||||||
|
|
||||||
|
// L Bumper
|
||||||
|
SecondaryDialConfig.DoubleButton(
|
||||||
|
/* sector */ 2,
|
||||||
|
0.2f,
|
||||||
|
ButtonConfig(
|
||||||
|
GamePadButtonInputId.LeftShoulder.ordinal,
|
||||||
|
"L",
|
||||||
|
true,
|
||||||
|
null,
|
||||||
|
"LeftBumper",
|
||||||
|
setOf(),
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
SecondaryDialConfig.RotationProcessor()
|
||||||
|
),
|
||||||
|
|
||||||
|
// ZL Trigger
|
||||||
|
SecondaryDialConfig.DoubleButton(
|
||||||
|
/* sector */ 2,
|
||||||
|
1f,
|
||||||
|
ButtonConfig(
|
||||||
|
GamePadButtonInputId.LeftTrigger.ordinal,
|
||||||
|
"ZL",
|
||||||
|
true,
|
||||||
|
null,
|
||||||
|
"LeftTrigger",
|
||||||
|
setOf(),
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
SecondaryDialConfig.RotationProcessor()
|
||||||
|
),
|
||||||
|
|
||||||
|
// L3 as a separate button
|
||||||
|
SecondaryDialConfig.SingleButton(
|
||||||
|
/* sector */ 1,
|
||||||
|
buttonScale,
|
||||||
|
1.0f,
|
||||||
|
ButtonConfig(
|
||||||
|
GamePadButtonInputId.LeftStickButton.ordinal,
|
||||||
|
"L3",
|
||||||
|
true,
|
||||||
|
null,
|
||||||
|
"LeftStickButton",
|
||||||
|
setOf(),
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
SecondaryDialConfig.RotationProcessor()
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return GamePadConfig(
|
||||||
|
/* ringSegments = */ 12,
|
||||||
|
/* Primary (ABXY) */
|
||||||
|
PrimaryDialConfig.PrimaryButtons(
|
||||||
|
if (useSwitchLayout) {
|
||||||
|
// Switch/Nintendo: A=Right, X=Top, Y=Left, B=Bottom
|
||||||
|
listOf(
|
||||||
|
ButtonConfig(GamePadButtonInputId.A.ordinal, "A", true, null, "A", setOf(), true, null), // Right
|
||||||
|
ButtonConfig(GamePadButtonInputId.X.ordinal, "X", true, null, "X", setOf(), true, null), // Top
|
||||||
|
ButtonConfig(GamePadButtonInputId.Y.ordinal, "Y", true, null, "Y", setOf(), true, null), // Left
|
||||||
|
ButtonConfig(GamePadButtonInputId.B.ordinal, "B", true, null, "B", setOf(), true, null) // Bottom
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
// Xbox Style: B=Right, Y=Top, X=Left, A=Bottom
|
||||||
|
listOf(
|
||||||
|
ButtonConfig(GamePadButtonInputId.B.ordinal, "B", true, null, "B", setOf(), true, null), // Right
|
||||||
|
ButtonConfig(GamePadButtonInputId.Y.ordinal, "Y", true, null, "Y", setOf(), true, null), // Top
|
||||||
|
ButtonConfig(GamePadButtonInputId.X.ordinal, "X", true, null, "X", setOf(), true, null), // Left
|
||||||
|
ButtonConfig(GamePadButtonInputId.A.ordinal, "A", true, null, "A", setOf(), true, null) // Bottom
|
||||||
|
)
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
0f,
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
listOf(
|
||||||
|
// Right stick
|
||||||
|
// IMPORTANT: pressButtonId -> DUMMY_RIGHT_STICK_PRESS_ID, so that double tap does not trigger R3
|
||||||
|
SecondaryDialConfig.Stick(
|
||||||
|
/* sector */ 6,
|
||||||
|
/* size */ 3,
|
||||||
|
/* gap */ 2.7f,
|
||||||
|
distance,
|
||||||
|
GamePadButtonInputId.RightStick.ordinal,
|
||||||
|
DUMMY_RIGHT_STICK_PRESS_ID,
|
||||||
|
null,
|
||||||
|
setOf(),
|
||||||
|
"RightStick",
|
||||||
|
SecondaryDialConfig.RotationProcessor()
|
||||||
|
),
|
||||||
|
|
||||||
|
// Plus
|
||||||
|
SecondaryDialConfig.SingleButton(
|
||||||
|
/* sector */ 9,
|
||||||
|
buttonScale,
|
||||||
|
0.3f,
|
||||||
|
ButtonConfig(
|
||||||
|
GamePadButtonInputId.Plus.ordinal,
|
||||||
|
"+",
|
||||||
|
true,
|
||||||
|
null,
|
||||||
|
"Plus",
|
||||||
|
setOf(),
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
SecondaryDialConfig.RotationProcessor()
|
||||||
|
),
|
||||||
|
|
||||||
|
// R Bumper
|
||||||
|
SecondaryDialConfig.DoubleButton(
|
||||||
|
/* sector */ 3,
|
||||||
|
0.2f,
|
||||||
|
ButtonConfig(
|
||||||
|
GamePadButtonInputId.RightShoulder.ordinal,
|
||||||
|
"R",
|
||||||
|
true,
|
||||||
|
null,
|
||||||
|
"RightBumper",
|
||||||
|
setOf(),
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
SecondaryDialConfig.RotationProcessor()
|
||||||
|
),
|
||||||
|
|
||||||
|
// ZR Trigger
|
||||||
|
SecondaryDialConfig.DoubleButton(
|
||||||
|
/* sector */ 3,
|
||||||
|
1f,
|
||||||
|
ButtonConfig(
|
||||||
|
GamePadButtonInputId.RightTrigger.ordinal,
|
||||||
|
"ZR",
|
||||||
|
true,
|
||||||
|
null,
|
||||||
|
"RightTrigger",
|
||||||
|
setOf(),
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
SecondaryDialConfig.RotationProcessor()
|
||||||
|
),
|
||||||
|
|
||||||
|
// R3 as a separate button
|
||||||
|
SecondaryDialConfig.SingleButton(
|
||||||
|
/* sector */ 5,
|
||||||
|
buttonScale,
|
||||||
|
1.0f,
|
||||||
|
ButtonConfig(
|
||||||
|
GamePadButtonInputId.RightStickButton.ordinal,
|
||||||
|
"R3",
|
||||||
|
true,
|
||||||
|
null,
|
||||||
|
"RightStickButton",
|
||||||
|
setOf(),
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
SecondaryDialConfig.RotationProcessor()
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,410 @@
|
|||||||
|
package org.kenjinx.android
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Context
|
||||||
|
import android.view.KeyEvent
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.FrameLayout
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.viewinterop.AndroidView
|
||||||
|
import androidx.core.math.MathUtils
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import com.swordfish.radialgamepad.library.RadialGamePad
|
||||||
|
import com.swordfish.radialgamepad.library.config.ButtonConfig
|
||||||
|
import com.swordfish.radialgamepad.library.config.CrossConfig
|
||||||
|
import com.swordfish.radialgamepad.library.config.CrossContentDescription
|
||||||
|
import com.swordfish.radialgamepad.library.config.PrimaryDialConfig
|
||||||
|
import com.swordfish.radialgamepad.library.config.SecondaryDialConfig
|
||||||
|
import com.swordfish.radialgamepad.library.event.Event
|
||||||
|
import kotlinx.coroutines.flow.merge
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import org.kenjinx.android.viewmodels.MainViewModel
|
||||||
|
import org.kenjinx.android.viewmodels.QuickSettings
|
||||||
|
|
||||||
|
private const val DUMMY_LEFT_STICK_PRESS_ID = 10001
|
||||||
|
private const val DUMMY_RIGHT_STICK_PRESS_ID = 10002
|
||||||
|
|
||||||
|
class GameController4(var activity: Activity) : IGameController {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private fun init(context: Context, controller: GameController4): View {
|
||||||
|
val inflater = LayoutInflater.from(context)
|
||||||
|
val parent = FrameLayout(context)
|
||||||
|
val view = inflater.inflate(R.layout.game_layout, parent, false)
|
||||||
|
|
||||||
|
val leftContainer = view.findViewById<FrameLayout>(R.id.leftcontainer)!!
|
||||||
|
val rightContainer = view.findViewById<FrameLayout>(R.id.rightcontainer)!!
|
||||||
|
|
||||||
|
leftContainer.addView(controller.leftGamePad)
|
||||||
|
rightContainer.addView(controller.rightGamePad)
|
||||||
|
|
||||||
|
leftContainer.addOnLayoutChangeListener { v, _, _, _, _, _, _, _, _ ->
|
||||||
|
val scale = QuickSettings(controller.activity).controllerScale.coerceIn(0.5f, 1.5f)
|
||||||
|
v.pivotX = 0f
|
||||||
|
v.pivotY = v.height.toFloat()
|
||||||
|
v.scaleX = scale
|
||||||
|
v.scaleY = scale
|
||||||
|
}
|
||||||
|
rightContainer.addOnLayoutChangeListener { v, _, _, _, _, _, _, _, _ ->
|
||||||
|
val scale = QuickSettings(controller.activity).controllerScale.coerceIn(0.5f, 1.5f)
|
||||||
|
v.pivotX = v.width.toFloat()
|
||||||
|
v.pivotY = v.height.toFloat()
|
||||||
|
v.scaleX = scale
|
||||||
|
v.scaleY = scale
|
||||||
|
}
|
||||||
|
|
||||||
|
leftContainer.post { leftContainer.requestLayout() }
|
||||||
|
rightContainer.post { rightContainer.requestLayout() }
|
||||||
|
|
||||||
|
return view
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun Compose(viewModel: MainViewModel) {
|
||||||
|
AndroidView(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
factory = { context ->
|
||||||
|
val controller = GameController4(viewModel.activity)
|
||||||
|
val c = init(context, controller)
|
||||||
|
|
||||||
|
viewModel.activity.lifecycleScope.launch {
|
||||||
|
val events = merge(
|
||||||
|
controller.leftGamePad.events(),
|
||||||
|
controller.rightGamePad.events()
|
||||||
|
)
|
||||||
|
events.safeCollect { controller.handleEvent(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
controller.controllerView = c
|
||||||
|
viewModel.setGameController(controller)
|
||||||
|
controller.setVisible(QuickSettings(viewModel.activity).useVirtualController)
|
||||||
|
c
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var controllerView: View? = null
|
||||||
|
var leftGamePad: RadialGamePad
|
||||||
|
var rightGamePad: RadialGamePad
|
||||||
|
var controllerId: Int = -1
|
||||||
|
override val isVisible: Boolean
|
||||||
|
get() = controllerView?.isVisible ?: false
|
||||||
|
|
||||||
|
init {
|
||||||
|
val useSwitchLayout = QuickSettings(activity).useSwitchLayout
|
||||||
|
leftGamePad = RadialGamePad(generateConfig4(true, useSwitchLayout), 16f, activity)
|
||||||
|
rightGamePad = RadialGamePad(generateConfig4(false, useSwitchLayout), 16f, activity)
|
||||||
|
|
||||||
|
leftGamePad.primaryDialMaxSizeDp = 200f
|
||||||
|
rightGamePad.primaryDialMaxSizeDp = 200f
|
||||||
|
|
||||||
|
leftGamePad.gravityX = -1f
|
||||||
|
leftGamePad.gravityY = 1f
|
||||||
|
rightGamePad.gravityX = 1f
|
||||||
|
rightGamePad.gravityY = 1f
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setVisible(isVisible: Boolean) {
|
||||||
|
controllerView?.apply {
|
||||||
|
this.isVisible = isVisible
|
||||||
|
if (isVisible) connect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun connect() {
|
||||||
|
if (controllerId == -1)
|
||||||
|
controllerId = KenjinxNative.inputConnectGamepad(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleEvent(ev: Event) {
|
||||||
|
if (controllerId == -1)
|
||||||
|
controllerId = KenjinxNative.inputConnectGamepad(0)
|
||||||
|
|
||||||
|
controllerId.apply {
|
||||||
|
when (ev) {
|
||||||
|
is Event.Button -> {
|
||||||
|
if (ev.id == DUMMY_LEFT_STICK_PRESS_ID || ev.id == DUMMY_RIGHT_STICK_PRESS_ID) return
|
||||||
|
when (ev.action) {
|
||||||
|
KeyEvent.ACTION_UP -> KenjinxNative.inputSetButtonReleased(ev.id, this)
|
||||||
|
KeyEvent.ACTION_DOWN -> KenjinxNative.inputSetButtonPressed(ev.id, this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is Event.Direction -> {
|
||||||
|
when (ev.id) {
|
||||||
|
GamePadButtonInputId.DpadUp.ordinal -> {
|
||||||
|
if (ev.xAxis > 0) {
|
||||||
|
KenjinxNative.inputSetButtonPressed(GamePadButtonInputId.DpadRight.ordinal, this)
|
||||||
|
KenjinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadLeft.ordinal, this)
|
||||||
|
} else if (ev.xAxis < 0) {
|
||||||
|
KenjinxNative.inputSetButtonPressed(GamePadButtonInputId.DpadLeft.ordinal, this)
|
||||||
|
KenjinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadRight.ordinal, this)
|
||||||
|
} else {
|
||||||
|
KenjinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadLeft.ordinal, this)
|
||||||
|
KenjinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadRight.ordinal, this)
|
||||||
|
}
|
||||||
|
if (ev.yAxis < 0) {
|
||||||
|
KenjinxNative.inputSetButtonPressed(GamePadButtonInputId.DpadUp.ordinal, this)
|
||||||
|
KenjinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadDown.ordinal, this)
|
||||||
|
} else if (ev.yAxis > 0) {
|
||||||
|
KenjinxNative.inputSetButtonPressed(GamePadButtonInputId.DpadDown.ordinal, this)
|
||||||
|
KenjinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadUp.ordinal, this)
|
||||||
|
} else {
|
||||||
|
KenjinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadDown.ordinal, this)
|
||||||
|
KenjinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadUp.ordinal, this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GamePadButtonInputId.LeftStick.ordinal -> {
|
||||||
|
val setting = QuickSettings(activity)
|
||||||
|
val x = MathUtils.clamp(ev.xAxis * setting.controllerStickSensitivity, -1f, 1f)
|
||||||
|
val y = MathUtils.clamp(ev.yAxis * setting.controllerStickSensitivity, -1f, 1f)
|
||||||
|
KenjinxNative.inputSetStickAxis(1, x, -y, this)
|
||||||
|
}
|
||||||
|
GamePadButtonInputId.RightStick.ordinal -> {
|
||||||
|
val setting = QuickSettings(activity)
|
||||||
|
val x = MathUtils.clamp(ev.xAxis * setting.controllerStickSensitivity, -1f, 1f)
|
||||||
|
val y = MathUtils.clamp(ev.yAxis * setting.controllerStickSensitivity, -1f, 1f)
|
||||||
|
KenjinxNative.inputSetStickAxis(2, x, -y, this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun generateConfig4(isLeft: Boolean, useSwitchLayout: Boolean): GamePadConfig {
|
||||||
|
val distance = 0.3f
|
||||||
|
val buttonScale = 1f
|
||||||
|
|
||||||
|
if (isLeft) {
|
||||||
|
return GamePadConfig(
|
||||||
|
/* ringSegments = */ 12,
|
||||||
|
/* Primary (Stick) */
|
||||||
|
// IMPORTANT: pressButtonId -> DUMMY_LEFT_STICK_PRESS_ID, so that double tap does not trigger L3
|
||||||
|
PrimaryDialConfig.Stick(
|
||||||
|
GamePadButtonInputId.LeftStick.ordinal,
|
||||||
|
DUMMY_LEFT_STICK_PRESS_ID,
|
||||||
|
setOf(),
|
||||||
|
"LeftStick",
|
||||||
|
null
|
||||||
|
),
|
||||||
|
listOf(
|
||||||
|
// D-Pad
|
||||||
|
SecondaryDialConfig.Cross(
|
||||||
|
/* sector */ 10,
|
||||||
|
/* size */ 3,
|
||||||
|
/* gap */ 2.1f,
|
||||||
|
distance,
|
||||||
|
CrossConfig(
|
||||||
|
GamePadButtonInputId.DpadUp.ordinal,
|
||||||
|
CrossConfig.Shape.STANDARD,
|
||||||
|
null,
|
||||||
|
setOf(),
|
||||||
|
CrossContentDescription(),
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
SecondaryDialConfig.RotationProcessor()
|
||||||
|
),
|
||||||
|
|
||||||
|
// Minus
|
||||||
|
SecondaryDialConfig.SingleButton(
|
||||||
|
/* sector */ 9,
|
||||||
|
buttonScale,
|
||||||
|
0.3f,
|
||||||
|
ButtonConfig(
|
||||||
|
GamePadButtonInputId.Minus.ordinal,
|
||||||
|
"-",
|
||||||
|
true,
|
||||||
|
null,
|
||||||
|
"Minus",
|
||||||
|
setOf(),
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
SecondaryDialConfig.RotationProcessor()
|
||||||
|
),
|
||||||
|
|
||||||
|
// L-Bumper
|
||||||
|
SecondaryDialConfig.DoubleButton(
|
||||||
|
/* sector */ 2,
|
||||||
|
1.2f,
|
||||||
|
ButtonConfig(
|
||||||
|
GamePadButtonInputId.LeftShoulder.ordinal,
|
||||||
|
"L",
|
||||||
|
true,
|
||||||
|
null,
|
||||||
|
"LeftBumper",
|
||||||
|
setOf(),
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
SecondaryDialConfig.RotationProcessor()
|
||||||
|
),
|
||||||
|
|
||||||
|
// ZL-Trigger
|
||||||
|
SecondaryDialConfig.DoubleButton(
|
||||||
|
/* sector */ 2,
|
||||||
|
2f,
|
||||||
|
ButtonConfig(
|
||||||
|
GamePadButtonInputId.LeftTrigger.ordinal,
|
||||||
|
"ZL",
|
||||||
|
true,
|
||||||
|
null,
|
||||||
|
"LeftTrigger",
|
||||||
|
setOf(),
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
SecondaryDialConfig.RotationProcessor()
|
||||||
|
),
|
||||||
|
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return GamePadConfig(
|
||||||
|
/* ringSegments = */ 12,
|
||||||
|
/* Primary (ABXY) */
|
||||||
|
PrimaryDialConfig.PrimaryButtons(
|
||||||
|
if (useSwitchLayout) {
|
||||||
|
// Switch/Nintendo: A=Right, X=Top, Y=Left, B=Bottom
|
||||||
|
listOf(
|
||||||
|
ButtonConfig(GamePadButtonInputId.A.ordinal, "A", true, null, "A", setOf(), true, null), // Right
|
||||||
|
ButtonConfig(GamePadButtonInputId.X.ordinal, "X", true, null, "X", setOf(), true, null), // Top
|
||||||
|
ButtonConfig(GamePadButtonInputId.Y.ordinal, "Y", true, null, "Y", setOf(), true, null), // Left
|
||||||
|
ButtonConfig(GamePadButtonInputId.B.ordinal, "B", true, null, "B", setOf(), true, null) // Bottom
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
// Xbox-Stil: B=Right, Y=Top, X=Left, A=Bottom
|
||||||
|
listOf(
|
||||||
|
ButtonConfig(GamePadButtonInputId.B.ordinal, "B", true, null, "B", setOf(), true, null), // Right
|
||||||
|
ButtonConfig(GamePadButtonInputId.Y.ordinal, "Y", true, null, "Y", setOf(), true, null), // Top
|
||||||
|
ButtonConfig(GamePadButtonInputId.X.ordinal, "X", true, null, "X", setOf(), true, null), // Left
|
||||||
|
ButtonConfig(GamePadButtonInputId.A.ordinal, "A", true, null, "A", setOf(), true, null) // Bottom
|
||||||
|
)
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
0f,
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
listOf(
|
||||||
|
// Right stick
|
||||||
|
// IMPORTANT: pressButtonId -> DUMMY_RIGHT_STICK_PRESS_ID, so that double tap does not trigger R3
|
||||||
|
SecondaryDialConfig.Stick(
|
||||||
|
/* sector */ 6,
|
||||||
|
/* size */ 3,
|
||||||
|
/* gap */ 2.7f,
|
||||||
|
distance,
|
||||||
|
GamePadButtonInputId.RightStick.ordinal,
|
||||||
|
DUMMY_RIGHT_STICK_PRESS_ID,
|
||||||
|
null,
|
||||||
|
setOf(),
|
||||||
|
"RightStick",
|
||||||
|
SecondaryDialConfig.RotationProcessor()
|
||||||
|
),
|
||||||
|
|
||||||
|
// Plus
|
||||||
|
SecondaryDialConfig.SingleButton(
|
||||||
|
/* sector */ 9,
|
||||||
|
buttonScale,
|
||||||
|
0.3f,
|
||||||
|
ButtonConfig(
|
||||||
|
GamePadButtonInputId.Plus.ordinal,
|
||||||
|
"+",
|
||||||
|
true,
|
||||||
|
null,
|
||||||
|
"Plus",
|
||||||
|
setOf(),
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
SecondaryDialConfig.RotationProcessor()
|
||||||
|
),
|
||||||
|
|
||||||
|
// R-Bumper
|
||||||
|
SecondaryDialConfig.DoubleButton(
|
||||||
|
/* sector */ 3,
|
||||||
|
1.2f,
|
||||||
|
ButtonConfig(
|
||||||
|
GamePadButtonInputId.RightShoulder.ordinal,
|
||||||
|
"R",
|
||||||
|
true,
|
||||||
|
null,
|
||||||
|
"RightBumper",
|
||||||
|
setOf(),
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
SecondaryDialConfig.RotationProcessor()
|
||||||
|
),
|
||||||
|
|
||||||
|
// ZR-Trigger
|
||||||
|
SecondaryDialConfig.DoubleButton(
|
||||||
|
/* sector */ 3,
|
||||||
|
2f,
|
||||||
|
ButtonConfig(
|
||||||
|
GamePadButtonInputId.RightTrigger.ordinal,
|
||||||
|
"ZR",
|
||||||
|
true,
|
||||||
|
null,
|
||||||
|
"RightTrigger",
|
||||||
|
setOf(),
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
SecondaryDialConfig.RotationProcessor()
|
||||||
|
),
|
||||||
|
|
||||||
|
// R3 separat
|
||||||
|
SecondaryDialConfig.SingleButton(
|
||||||
|
/* sector */ 3,
|
||||||
|
buttonScale,
|
||||||
|
0f,
|
||||||
|
ButtonConfig(
|
||||||
|
GamePadButtonInputId.RightStickButton.ordinal,
|
||||||
|
"R3",
|
||||||
|
true,
|
||||||
|
null,
|
||||||
|
"RightStickButton",
|
||||||
|
setOf(),
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
SecondaryDialConfig.RotationProcessor()
|
||||||
|
),
|
||||||
|
|
||||||
|
// L3 separat
|
||||||
|
SecondaryDialConfig.SingleButton(
|
||||||
|
/* sector */ 4,
|
||||||
|
buttonScale,
|
||||||
|
0f,
|
||||||
|
ButtonConfig(
|
||||||
|
GamePadButtonInputId.LeftStickButton.ordinal,
|
||||||
|
"L3",
|
||||||
|
true,
|
||||||
|
null,
|
||||||
|
"LeftStickButton",
|
||||||
|
setOf(),
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
SecondaryDialConfig.RotationProcessor()
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,414 @@
|
|||||||
|
package org.kenjinx.android
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Context
|
||||||
|
import android.view.KeyEvent
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.FrameLayout
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.viewinterop.AndroidView
|
||||||
|
import androidx.core.math.MathUtils
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import com.swordfish.radialgamepad.library.RadialGamePad
|
||||||
|
import com.swordfish.radialgamepad.library.config.ButtonConfig
|
||||||
|
import com.swordfish.radialgamepad.library.config.CrossConfig
|
||||||
|
import com.swordfish.radialgamepad.library.config.CrossContentDescription
|
||||||
|
import com.swordfish.radialgamepad.library.config.PrimaryDialConfig
|
||||||
|
import com.swordfish.radialgamepad.library.config.SecondaryDialConfig
|
||||||
|
import com.swordfish.radialgamepad.library.event.Event
|
||||||
|
import kotlinx.coroutines.flow.merge
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import org.kenjinx.android.viewmodels.MainViewModel
|
||||||
|
import org.kenjinx.android.viewmodels.QuickSettings
|
||||||
|
|
||||||
|
private const val DUMMY_LEFT_STICK_PRESS_ID = 10001
|
||||||
|
private const val DUMMY_RIGHT_STICK_PRESS_ID = 10002
|
||||||
|
|
||||||
|
class GameController5(var activity: Activity) : IGameController {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private fun init(context: Context, controller: GameController5): View {
|
||||||
|
val inflater = LayoutInflater.from(context)
|
||||||
|
val parent = FrameLayout(context)
|
||||||
|
val view = inflater.inflate(R.layout.game_layout, parent, false)
|
||||||
|
|
||||||
|
val leftContainer = view.findViewById<FrameLayout>(R.id.leftcontainer)!!
|
||||||
|
val rightContainer = view.findViewById<FrameLayout>(R.id.rightcontainer)!!
|
||||||
|
|
||||||
|
leftContainer.addView(controller.leftGamePad)
|
||||||
|
rightContainer.addView(controller.rightGamePad)
|
||||||
|
|
||||||
|
leftContainer.addOnLayoutChangeListener { v, _, _, _, _, _, _, _, _ ->
|
||||||
|
val scale = QuickSettings(controller.activity).controllerScale.coerceIn(0.5f, 1.5f)
|
||||||
|
v.pivotX = 0f
|
||||||
|
v.pivotY = v.height.toFloat()
|
||||||
|
v.scaleX = scale
|
||||||
|
v.scaleY = scale
|
||||||
|
}
|
||||||
|
rightContainer.addOnLayoutChangeListener { v, _, _, _, _, _, _, _, _ ->
|
||||||
|
val scale = QuickSettings(controller.activity).controllerScale.coerceIn(0.5f, 1.5f)
|
||||||
|
v.pivotX = v.width.toFloat()
|
||||||
|
v.pivotY = v.height.toFloat()
|
||||||
|
v.scaleX = scale
|
||||||
|
v.scaleY = scale
|
||||||
|
}
|
||||||
|
|
||||||
|
leftContainer.post { leftContainer.requestLayout() }
|
||||||
|
rightContainer.post { rightContainer.requestLayout() }
|
||||||
|
|
||||||
|
return view
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun Compose(viewModel: MainViewModel) {
|
||||||
|
AndroidView(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
factory = { context ->
|
||||||
|
val controller = GameController5(viewModel.activity)
|
||||||
|
val c = init(context, controller)
|
||||||
|
|
||||||
|
viewModel.activity.lifecycleScope.launch {
|
||||||
|
val events = merge(
|
||||||
|
controller.leftGamePad.events(),
|
||||||
|
controller.rightGamePad.events()
|
||||||
|
)
|
||||||
|
events.safeCollect { controller.handleEvent(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
controller.controllerView = c
|
||||||
|
viewModel.setGameController(controller)
|
||||||
|
controller.setVisible(QuickSettings(viewModel.activity).useVirtualController)
|
||||||
|
c
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var controllerView: View? = null
|
||||||
|
var leftGamePad: RadialGamePad
|
||||||
|
var rightGamePad: RadialGamePad
|
||||||
|
var controllerId: Int = -1
|
||||||
|
override val isVisible: Boolean
|
||||||
|
get() = controllerView?.isVisible ?: false
|
||||||
|
|
||||||
|
init {
|
||||||
|
val useSwitchLayout = QuickSettings(activity).useSwitchLayout
|
||||||
|
leftGamePad = RadialGamePad(generateConfig5(true, useSwitchLayout), 16f, activity)
|
||||||
|
rightGamePad = RadialGamePad(generateConfig5(false, useSwitchLayout), 16f, activity)
|
||||||
|
|
||||||
|
leftGamePad.primaryDialMaxSizeDp = 200f
|
||||||
|
rightGamePad.primaryDialMaxSizeDp = 200f
|
||||||
|
|
||||||
|
leftGamePad.gravityX = -1f
|
||||||
|
leftGamePad.gravityY = 1f
|
||||||
|
rightGamePad.gravityX = 1f
|
||||||
|
rightGamePad.gravityY = 1f
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setVisible(isVisible: Boolean) {
|
||||||
|
controllerView?.apply {
|
||||||
|
this.isVisible = isVisible
|
||||||
|
if (isVisible) connect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun connect() {
|
||||||
|
if (controllerId == -1)
|
||||||
|
controllerId = KenjinxNative.inputConnectGamepad(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleEvent(ev: Event) {
|
||||||
|
if (controllerId == -1)
|
||||||
|
controllerId = KenjinxNative.inputConnectGamepad(0)
|
||||||
|
|
||||||
|
controllerId.apply {
|
||||||
|
when (ev) {
|
||||||
|
is Event.Button -> {
|
||||||
|
if (ev.id == DUMMY_LEFT_STICK_PRESS_ID || ev.id == DUMMY_RIGHT_STICK_PRESS_ID) return
|
||||||
|
when (ev.action) {
|
||||||
|
KeyEvent.ACTION_UP -> KenjinxNative.inputSetButtonReleased(ev.id, this)
|
||||||
|
KeyEvent.ACTION_DOWN -> KenjinxNative.inputSetButtonPressed(ev.id, this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is Event.Direction -> {
|
||||||
|
when (ev.id) {
|
||||||
|
GamePadButtonInputId.DpadUp.ordinal -> {
|
||||||
|
if (ev.xAxis > 0) {
|
||||||
|
KenjinxNative.inputSetButtonPressed(GamePadButtonInputId.DpadRight.ordinal, this)
|
||||||
|
KenjinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadLeft.ordinal, this)
|
||||||
|
} else if (ev.xAxis < 0) {
|
||||||
|
KenjinxNative.inputSetButtonPressed(GamePadButtonInputId.DpadLeft.ordinal, this)
|
||||||
|
KenjinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadRight.ordinal, this)
|
||||||
|
} else {
|
||||||
|
KenjinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadLeft.ordinal, this)
|
||||||
|
KenjinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadRight.ordinal, this)
|
||||||
|
}
|
||||||
|
if (ev.yAxis < 0) {
|
||||||
|
KenjinxNative.inputSetButtonPressed(GamePadButtonInputId.DpadUp.ordinal, this)
|
||||||
|
KenjinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadDown.ordinal, this)
|
||||||
|
} else if (ev.yAxis > 0) {
|
||||||
|
KenjinxNative.inputSetButtonPressed(GamePadButtonInputId.DpadDown.ordinal, this)
|
||||||
|
KenjinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadUp.ordinal, this)
|
||||||
|
} else {
|
||||||
|
KenjinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadDown.ordinal, this)
|
||||||
|
KenjinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadUp.ordinal, this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GamePadButtonInputId.LeftStick.ordinal -> {
|
||||||
|
val setting = QuickSettings(activity)
|
||||||
|
val x = MathUtils.clamp(ev.xAxis * setting.controllerStickSensitivity, -1f, 1f)
|
||||||
|
val y = MathUtils.clamp(ev.yAxis * setting.controllerStickSensitivity, -1f, 1f)
|
||||||
|
KenjinxNative.inputSetStickAxis(1, x, -y, this)
|
||||||
|
}
|
||||||
|
GamePadButtonInputId.RightStick.ordinal -> {
|
||||||
|
val setting = QuickSettings(activity)
|
||||||
|
val x = MathUtils.clamp(ev.xAxis * setting.controllerStickSensitivity, -1f, 1f)
|
||||||
|
val y = MathUtils.clamp(ev.yAxis * setting.controllerStickSensitivity, -1f, 1f)
|
||||||
|
KenjinxNative.inputSetStickAxis(2, x, -y, this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun generateConfig5(isLeft: Boolean, useSwitchLayout: Boolean): GamePadConfig {
|
||||||
|
val distance = 0.3f
|
||||||
|
val buttonScale = 1f
|
||||||
|
|
||||||
|
if (isLeft) {
|
||||||
|
return GamePadConfig(
|
||||||
|
/* ringSegments = */ 12,
|
||||||
|
/* Primary (Stick) */
|
||||||
|
// IMPORTANT: pressButtonId -> DUMMY_LEFT_STICK_PRESS_ID, so that double tap does not trigger L3
|
||||||
|
PrimaryDialConfig.Stick(
|
||||||
|
GamePadButtonInputId.LeftStick.ordinal,
|
||||||
|
DUMMY_LEFT_STICK_PRESS_ID,
|
||||||
|
setOf(),
|
||||||
|
"LeftStick",
|
||||||
|
null
|
||||||
|
),
|
||||||
|
listOf(
|
||||||
|
// D-Pad
|
||||||
|
SecondaryDialConfig.Cross(
|
||||||
|
/* sector */ 10,
|
||||||
|
/* size */ 3,
|
||||||
|
/* gap */ 2.1f,
|
||||||
|
distance,
|
||||||
|
CrossConfig(
|
||||||
|
GamePadButtonInputId.DpadUp.ordinal,
|
||||||
|
CrossConfig.Shape.STANDARD,
|
||||||
|
null,
|
||||||
|
setOf(),
|
||||||
|
CrossContentDescription(),
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
SecondaryDialConfig.RotationProcessor()
|
||||||
|
),
|
||||||
|
|
||||||
|
// Minus
|
||||||
|
SecondaryDialConfig.SingleButton(
|
||||||
|
/* sector */ 9,
|
||||||
|
buttonScale,
|
||||||
|
0.3f,
|
||||||
|
ButtonConfig(
|
||||||
|
GamePadButtonInputId.Minus.ordinal,
|
||||||
|
"-",
|
||||||
|
true,
|
||||||
|
null,
|
||||||
|
"Minus",
|
||||||
|
setOf(),
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
SecondaryDialConfig.RotationProcessor()
|
||||||
|
),
|
||||||
|
|
||||||
|
// L Bumper
|
||||||
|
SecondaryDialConfig.SingleButton(
|
||||||
|
/* sector */ 3,
|
||||||
|
1.5f,
|
||||||
|
1.0f,
|
||||||
|
ButtonConfig(
|
||||||
|
GamePadButtonInputId.LeftShoulder.ordinal,
|
||||||
|
"L",
|
||||||
|
true,
|
||||||
|
null,
|
||||||
|
"LeftBumper",
|
||||||
|
setOf(),
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
SecondaryDialConfig.RotationProcessor()
|
||||||
|
),
|
||||||
|
|
||||||
|
// ZL Trigger
|
||||||
|
SecondaryDialConfig.SingleButton(
|
||||||
|
/* sector */ 2,
|
||||||
|
1.5f,
|
||||||
|
1.1f,
|
||||||
|
ButtonConfig(
|
||||||
|
GamePadButtonInputId.LeftTrigger.ordinal,
|
||||||
|
"ZL",
|
||||||
|
true,
|
||||||
|
null,
|
||||||
|
"LeftTrigger",
|
||||||
|
setOf(),
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
SecondaryDialConfig.RotationProcessor()
|
||||||
|
),
|
||||||
|
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return GamePadConfig(
|
||||||
|
/* ringSegments = */ 12,
|
||||||
|
/* Primary (ABXY) */
|
||||||
|
PrimaryDialConfig.PrimaryButtons(
|
||||||
|
if (useSwitchLayout) {
|
||||||
|
// Switch/Nintendo: A=Right, X=Top, Y=Left, B=Bottom
|
||||||
|
listOf(
|
||||||
|
ButtonConfig(GamePadButtonInputId.A.ordinal, "A", true, null, "A", setOf(), true, null), // Right
|
||||||
|
ButtonConfig(GamePadButtonInputId.X.ordinal, "X", true, null, "X", setOf(), true, null), // Top
|
||||||
|
ButtonConfig(GamePadButtonInputId.Y.ordinal, "Y", true, null, "Y", setOf(), true, null), // Left
|
||||||
|
ButtonConfig(GamePadButtonInputId.B.ordinal, "B", true, null, "B", setOf(), true, null) // Bottom
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
// Xbox-Stil: B=Right, Y=Top, X=Left, A=Bottom
|
||||||
|
listOf(
|
||||||
|
ButtonConfig(GamePadButtonInputId.B.ordinal, "B", true, null, "B", setOf(), true, null), // Right
|
||||||
|
ButtonConfig(GamePadButtonInputId.Y.ordinal, "Y", true, null, "Y", setOf(), true, null), // Top
|
||||||
|
ButtonConfig(GamePadButtonInputId.X.ordinal, "X", true, null, "X", setOf(), true, null), // Left
|
||||||
|
ButtonConfig(GamePadButtonInputId.A.ordinal, "A", true, null, "A", setOf(), true, null) // Bottom
|
||||||
|
)
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
0f,
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
listOf(
|
||||||
|
// Right stick
|
||||||
|
// IMPORTANT: pressButtonId -> DUMMY_RIGHT_STICK_PRESS_ID, so that double tap does not trigger R3
|
||||||
|
SecondaryDialConfig.Stick(
|
||||||
|
/* sector */ 6,
|
||||||
|
/* size */ 3,
|
||||||
|
/* gap */ 2.7f,
|
||||||
|
distance,
|
||||||
|
GamePadButtonInputId.RightStick.ordinal,
|
||||||
|
DUMMY_RIGHT_STICK_PRESS_ID,
|
||||||
|
null,
|
||||||
|
setOf(),
|
||||||
|
"RightStick",
|
||||||
|
SecondaryDialConfig.RotationProcessor()
|
||||||
|
),
|
||||||
|
|
||||||
|
// Plus
|
||||||
|
SecondaryDialConfig.SingleButton(
|
||||||
|
/* sector */ 9,
|
||||||
|
buttonScale,
|
||||||
|
0.3f,
|
||||||
|
ButtonConfig(
|
||||||
|
GamePadButtonInputId.Plus.ordinal,
|
||||||
|
"+",
|
||||||
|
true,
|
||||||
|
null,
|
||||||
|
"Plus",
|
||||||
|
setOf(),
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
SecondaryDialConfig.RotationProcessor()
|
||||||
|
),
|
||||||
|
|
||||||
|
// R Bumper
|
||||||
|
SecondaryDialConfig.SingleButton(
|
||||||
|
/* sector */ 3,
|
||||||
|
1.5f,
|
||||||
|
1.0f,
|
||||||
|
ButtonConfig(
|
||||||
|
GamePadButtonInputId.RightShoulder.ordinal,
|
||||||
|
"R",
|
||||||
|
true,
|
||||||
|
null,
|
||||||
|
"RightBumper",
|
||||||
|
setOf(),
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
SecondaryDialConfig.RotationProcessor()
|
||||||
|
),
|
||||||
|
|
||||||
|
// ZR Trigger
|
||||||
|
SecondaryDialConfig.SingleButton(
|
||||||
|
/* sector */ 4,
|
||||||
|
1.5f,
|
||||||
|
1.1f,
|
||||||
|
ButtonConfig(
|
||||||
|
GamePadButtonInputId.RightTrigger.ordinal,
|
||||||
|
"ZR",
|
||||||
|
true,
|
||||||
|
null,
|
||||||
|
"RightTrigger",
|
||||||
|
setOf(),
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
SecondaryDialConfig.RotationProcessor()
|
||||||
|
),
|
||||||
|
|
||||||
|
// R3 as a separate button
|
||||||
|
SecondaryDialConfig.SingleButton(
|
||||||
|
/* sector */ 3,
|
||||||
|
1.1f,
|
||||||
|
0f,
|
||||||
|
ButtonConfig(
|
||||||
|
GamePadButtonInputId.RightStickButton.ordinal,
|
||||||
|
"R3",
|
||||||
|
true,
|
||||||
|
null,
|
||||||
|
"RightStickButton",
|
||||||
|
setOf(),
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
SecondaryDialConfig.RotationProcessor()
|
||||||
|
),
|
||||||
|
|
||||||
|
// L3 as a separate button
|
||||||
|
SecondaryDialConfig.SingleButton(
|
||||||
|
/* sector */ 4,
|
||||||
|
1.1f,
|
||||||
|
0f,
|
||||||
|
ButtonConfig(
|
||||||
|
GamePadButtonInputId.LeftStickButton.ordinal,
|
||||||
|
"L3",
|
||||||
|
true,
|
||||||
|
null,
|
||||||
|
"LeftStickButton",
|
||||||
|
setOf(),
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
SecondaryDialConfig.RotationProcessor()
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,411 @@
|
|||||||
|
package org.kenjinx.android
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Context
|
||||||
|
import android.view.KeyEvent
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.FrameLayout
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.viewinterop.AndroidView
|
||||||
|
import androidx.core.math.MathUtils
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import com.swordfish.radialgamepad.library.RadialGamePad
|
||||||
|
import com.swordfish.radialgamepad.library.config.ButtonConfig
|
||||||
|
import com.swordfish.radialgamepad.library.config.CrossConfig
|
||||||
|
import com.swordfish.radialgamepad.library.config.CrossContentDescription
|
||||||
|
import com.swordfish.radialgamepad.library.config.PrimaryDialConfig
|
||||||
|
import com.swordfish.radialgamepad.library.config.SecondaryDialConfig
|
||||||
|
import com.swordfish.radialgamepad.library.event.Event
|
||||||
|
import kotlinx.coroutines.flow.merge
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import org.kenjinx.android.viewmodels.MainViewModel
|
||||||
|
import org.kenjinx.android.viewmodels.QuickSettings
|
||||||
|
|
||||||
|
private const val DUMMY_LEFT_STICK_PRESS_ID = 10001
|
||||||
|
private const val DUMMY_RIGHT_STICK_PRESS_ID = 10002
|
||||||
|
|
||||||
|
class GameController6(var activity: Activity) : IGameController {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private fun init(context: Context, controller: GameController6): View {
|
||||||
|
val inflater = LayoutInflater.from(context)
|
||||||
|
val parent = FrameLayout(context)
|
||||||
|
val view = inflater.inflate(R.layout.game_layout, parent, false)
|
||||||
|
|
||||||
|
val leftContainer = view.findViewById<FrameLayout>(R.id.leftcontainer)!!
|
||||||
|
val rightContainer = view.findViewById<FrameLayout>(R.id.rightcontainer)!!
|
||||||
|
|
||||||
|
leftContainer.addView(controller.leftGamePad)
|
||||||
|
rightContainer.addView(controller.rightGamePad)
|
||||||
|
|
||||||
|
leftContainer.addOnLayoutChangeListener { v, _, _, _, _, _, _, _, _ ->
|
||||||
|
val scale = QuickSettings(controller.activity).controllerScale.coerceIn(0.5f, 1.5f)
|
||||||
|
v.pivotX = 0f
|
||||||
|
v.pivotY = v.height.toFloat()
|
||||||
|
v.scaleX = scale
|
||||||
|
v.scaleY = scale
|
||||||
|
}
|
||||||
|
rightContainer.addOnLayoutChangeListener { v, _, _, _, _, _, _, _, _ ->
|
||||||
|
val scale = QuickSettings(controller.activity).controllerScale.coerceIn(0.5f, 1.5f)
|
||||||
|
v.pivotX = v.width.toFloat()
|
||||||
|
v.pivotY = v.height.toFloat()
|
||||||
|
v.scaleX = scale
|
||||||
|
v.scaleY = scale
|
||||||
|
}
|
||||||
|
|
||||||
|
leftContainer.post { leftContainer.requestLayout() }
|
||||||
|
rightContainer.post { rightContainer.requestLayout() }
|
||||||
|
|
||||||
|
return view
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun Compose(viewModel: MainViewModel) {
|
||||||
|
AndroidView(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
factory = { context ->
|
||||||
|
val controller = GameController6(viewModel.activity)
|
||||||
|
val c = init(context, controller)
|
||||||
|
|
||||||
|
viewModel.activity.lifecycleScope.launch {
|
||||||
|
val events = merge(
|
||||||
|
controller.leftGamePad.events(),
|
||||||
|
controller.rightGamePad.events()
|
||||||
|
)
|
||||||
|
events.safeCollect { controller.handleEvent(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
controller.controllerView = c
|
||||||
|
viewModel.setGameController(controller)
|
||||||
|
controller.setVisible(QuickSettings(viewModel.activity).useVirtualController)
|
||||||
|
c
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var controllerView: View? = null
|
||||||
|
var leftGamePad: RadialGamePad
|
||||||
|
var rightGamePad: RadialGamePad
|
||||||
|
var controllerId: Int = -1
|
||||||
|
override val isVisible: Boolean
|
||||||
|
get() = controllerView?.isVisible ?: false
|
||||||
|
|
||||||
|
init {
|
||||||
|
val useSwitchLayout = QuickSettings(activity).useSwitchLayout
|
||||||
|
leftGamePad = RadialGamePad(generateConfig6(true, useSwitchLayout), 16f, activity)
|
||||||
|
rightGamePad = RadialGamePad(generateConfig6(false, useSwitchLayout), 16f, activity)
|
||||||
|
|
||||||
|
leftGamePad.primaryDialMaxSizeDp = 200f
|
||||||
|
rightGamePad.primaryDialMaxSizeDp = 200f
|
||||||
|
|
||||||
|
leftGamePad.gravityX = -1f
|
||||||
|
leftGamePad.gravityY = 1f
|
||||||
|
rightGamePad.gravityX = 1f
|
||||||
|
rightGamePad.gravityY = 1f
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setVisible(isVisible: Boolean) {
|
||||||
|
controllerView?.apply {
|
||||||
|
this.isVisible = isVisible
|
||||||
|
if (isVisible) connect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun connect() {
|
||||||
|
if (controllerId == -1)
|
||||||
|
controllerId = KenjinxNative.inputConnectGamepad(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleEvent(ev: Event) {
|
||||||
|
if (controllerId == -1)
|
||||||
|
controllerId = KenjinxNative.inputConnectGamepad(0)
|
||||||
|
|
||||||
|
controllerId.apply {
|
||||||
|
when (ev) {
|
||||||
|
is Event.Button -> {
|
||||||
|
if (ev.id == DUMMY_LEFT_STICK_PRESS_ID || ev.id == DUMMY_RIGHT_STICK_PRESS_ID) return
|
||||||
|
when (ev.action) {
|
||||||
|
KeyEvent.ACTION_UP -> KenjinxNative.inputSetButtonReleased(ev.id, this)
|
||||||
|
KeyEvent.ACTION_DOWN -> KenjinxNative.inputSetButtonPressed(ev.id, this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is Event.Direction -> {
|
||||||
|
when (ev.id) {
|
||||||
|
GamePadButtonInputId.DpadUp.ordinal -> {
|
||||||
|
if (ev.xAxis > 0) {
|
||||||
|
KenjinxNative.inputSetButtonPressed(GamePadButtonInputId.DpadRight.ordinal, this)
|
||||||
|
KenjinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadLeft.ordinal, this)
|
||||||
|
} else if (ev.xAxis < 0) {
|
||||||
|
KenjinxNative.inputSetButtonPressed(GamePadButtonInputId.DpadLeft.ordinal, this)
|
||||||
|
KenjinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadRight.ordinal, this)
|
||||||
|
} else {
|
||||||
|
KenjinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadLeft.ordinal, this)
|
||||||
|
KenjinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadRight.ordinal, this)
|
||||||
|
}
|
||||||
|
if (ev.yAxis < 0) {
|
||||||
|
KenjinxNative.inputSetButtonPressed(GamePadButtonInputId.DpadUp.ordinal, this)
|
||||||
|
KenjinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadDown.ordinal, this)
|
||||||
|
} else if (ev.yAxis > 0) {
|
||||||
|
KenjinxNative.inputSetButtonPressed(GamePadButtonInputId.DpadDown.ordinal, this)
|
||||||
|
KenjinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadUp.ordinal, this)
|
||||||
|
} else {
|
||||||
|
KenjinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadDown.ordinal, this)
|
||||||
|
KenjinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadUp.ordinal, this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GamePadButtonInputId.LeftStick.ordinal -> {
|
||||||
|
val setting = QuickSettings(activity)
|
||||||
|
val x = MathUtils.clamp(ev.xAxis * setting.controllerStickSensitivity, -1f, 1f)
|
||||||
|
val y = MathUtils.clamp(ev.yAxis * setting.controllerStickSensitivity, -1f, 1f)
|
||||||
|
KenjinxNative.inputSetStickAxis(1, x, -y, this)
|
||||||
|
}
|
||||||
|
GamePadButtonInputId.RightStick.ordinal -> {
|
||||||
|
val setting = QuickSettings(activity)
|
||||||
|
val x = MathUtils.clamp(ev.xAxis * setting.controllerStickSensitivity, -1f, 1f)
|
||||||
|
val y = MathUtils.clamp(ev.yAxis * setting.controllerStickSensitivity, -1f, 1f)
|
||||||
|
KenjinxNative.inputSetStickAxis(2, x, -y, this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun generateConfig6(isLeft: Boolean, useSwitchLayout: Boolean): GamePadConfig {
|
||||||
|
val distance = 0.3f
|
||||||
|
val buttonScale = 1f
|
||||||
|
|
||||||
|
if (isLeft) {
|
||||||
|
return GamePadConfig(
|
||||||
|
/* ringSegments = */ 12,
|
||||||
|
/* Primary (Stick) */
|
||||||
|
// IMPORTANT: pressButtonId -> DUMMY_LEFT_STICK_PRESS_ID, so that double tap does not trigger L3
|
||||||
|
PrimaryDialConfig.Stick(
|
||||||
|
GamePadButtonInputId.LeftStick.ordinal,
|
||||||
|
DUMMY_LEFT_STICK_PRESS_ID,
|
||||||
|
setOf(),
|
||||||
|
"LeftStick",
|
||||||
|
null
|
||||||
|
),
|
||||||
|
listOf(
|
||||||
|
// D-Pad
|
||||||
|
SecondaryDialConfig.Cross(
|
||||||
|
/* sector */ 10,
|
||||||
|
/* size */ 3,
|
||||||
|
/* gap */ 2.5f,
|
||||||
|
distance,
|
||||||
|
CrossConfig(
|
||||||
|
GamePadButtonInputId.DpadUp.ordinal,
|
||||||
|
CrossConfig.Shape.STANDARD,
|
||||||
|
null,
|
||||||
|
setOf(),
|
||||||
|
CrossContentDescription(),
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
SecondaryDialConfig.RotationProcessor()
|
||||||
|
),
|
||||||
|
|
||||||
|
// Minus
|
||||||
|
SecondaryDialConfig.SingleButton(
|
||||||
|
/* sector */ 1,
|
||||||
|
buttonScale,
|
||||||
|
2f,
|
||||||
|
ButtonConfig(
|
||||||
|
GamePadButtonInputId.Minus.ordinal,
|
||||||
|
"-",
|
||||||
|
true,
|
||||||
|
null,
|
||||||
|
"Minus",
|
||||||
|
setOf(),
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
SecondaryDialConfig.RotationProcessor()
|
||||||
|
),
|
||||||
|
|
||||||
|
// L Bumper
|
||||||
|
SecondaryDialConfig.DoubleButton(
|
||||||
|
/* sector */ 2,
|
||||||
|
distance,
|
||||||
|
ButtonConfig(
|
||||||
|
GamePadButtonInputId.LeftShoulder.ordinal,
|
||||||
|
"L",
|
||||||
|
true,
|
||||||
|
null,
|
||||||
|
"LeftBumper",
|
||||||
|
setOf(),
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
SecondaryDialConfig.RotationProcessor()
|
||||||
|
),
|
||||||
|
|
||||||
|
// ZL Trigger
|
||||||
|
SecondaryDialConfig.SingleButton(
|
||||||
|
/* sector */ 9,
|
||||||
|
buttonScale,
|
||||||
|
distance,
|
||||||
|
ButtonConfig(
|
||||||
|
GamePadButtonInputId.LeftTrigger.ordinal,
|
||||||
|
"ZL",
|
||||||
|
true,
|
||||||
|
null,
|
||||||
|
"LeftTrigger",
|
||||||
|
setOf(),
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
SecondaryDialConfig.RotationProcessor()
|
||||||
|
),
|
||||||
|
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return GamePadConfig(
|
||||||
|
/* ringSegments = */ 12,
|
||||||
|
/* Primary (ABXY) */
|
||||||
|
PrimaryDialConfig.PrimaryButtons(
|
||||||
|
if (useSwitchLayout) {
|
||||||
|
// Switch/Nintendo: A=Right, X=Top, Y=Left, B=Bottom
|
||||||
|
listOf(
|
||||||
|
ButtonConfig(GamePadButtonInputId.A.ordinal, "A", true, null, "A", setOf(), true, null), // Right
|
||||||
|
ButtonConfig(GamePadButtonInputId.X.ordinal, "X", true, null, "X", setOf(), true, null), // Top
|
||||||
|
ButtonConfig(GamePadButtonInputId.Y.ordinal, "Y", true, null, "Y", setOf(), true, null), // Left
|
||||||
|
ButtonConfig(GamePadButtonInputId.B.ordinal, "B", true, null, "B", setOf(), true, null) // Bottom
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
// Xbox Style: B=Right, Y=Top, X=Left, A=Bottom
|
||||||
|
listOf(
|
||||||
|
ButtonConfig(GamePadButtonInputId.B.ordinal, "B", true, null, "B", setOf(), true, null), // Right
|
||||||
|
ButtonConfig(GamePadButtonInputId.Y.ordinal, "Y", true, null, "Y", setOf(), true, null), // Top
|
||||||
|
ButtonConfig(GamePadButtonInputId.X.ordinal, "X", true, null, "X", setOf(), true, null), // Left
|
||||||
|
ButtonConfig(GamePadButtonInputId.A.ordinal, "A", true, null, "A", setOf(), true, null) // Bottom
|
||||||
|
)
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
0f,
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
listOf(
|
||||||
|
// Right stick (unchanged)
|
||||||
|
// IMPORTANT: pressButtonId -> DUMMY_RIGHT_STICK_PRESS_ID, so that double tap does not trigger R3
|
||||||
|
SecondaryDialConfig.Stick(
|
||||||
|
/* sector */ 6,
|
||||||
|
/* size */ 3,
|
||||||
|
/* gap */ 2.5f,
|
||||||
|
0.7f,
|
||||||
|
GamePadButtonInputId.RightStick.ordinal,
|
||||||
|
DUMMY_RIGHT_STICK_PRESS_ID,
|
||||||
|
null,
|
||||||
|
setOf(),
|
||||||
|
"RightStick",
|
||||||
|
SecondaryDialConfig.RotationProcessor()
|
||||||
|
),
|
||||||
|
|
||||||
|
// Plus
|
||||||
|
SecondaryDialConfig.SingleButton(
|
||||||
|
/* sector */ 5,
|
||||||
|
buttonScale,
|
||||||
|
2f,
|
||||||
|
ButtonConfig(
|
||||||
|
GamePadButtonInputId.Plus.ordinal,
|
||||||
|
"+",
|
||||||
|
true,
|
||||||
|
null,
|
||||||
|
"Plus",
|
||||||
|
setOf(),
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
SecondaryDialConfig.RotationProcessor()
|
||||||
|
),
|
||||||
|
|
||||||
|
// R Bumper
|
||||||
|
SecondaryDialConfig.DoubleButton(
|
||||||
|
/* sector */ 3,
|
||||||
|
distance,
|
||||||
|
ButtonConfig(
|
||||||
|
GamePadButtonInputId.RightShoulder.ordinal,
|
||||||
|
"R",
|
||||||
|
true,
|
||||||
|
null,
|
||||||
|
"RightBumper",
|
||||||
|
setOf(),
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
SecondaryDialConfig.RotationProcessor()
|
||||||
|
),
|
||||||
|
|
||||||
|
// ZR Trigger
|
||||||
|
SecondaryDialConfig.SingleButton(
|
||||||
|
/* sector */ 9,
|
||||||
|
buttonScale,
|
||||||
|
distance,
|
||||||
|
ButtonConfig(
|
||||||
|
GamePadButtonInputId.RightTrigger.ordinal,
|
||||||
|
"ZR",
|
||||||
|
true,
|
||||||
|
null,
|
||||||
|
"RightTrigger",
|
||||||
|
setOf(),
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
SecondaryDialConfig.RotationProcessor()
|
||||||
|
),
|
||||||
|
|
||||||
|
// R3 as a separate button
|
||||||
|
SecondaryDialConfig.SingleButton(
|
||||||
|
/* sector */ 5,
|
||||||
|
buttonScale,
|
||||||
|
0.0f,
|
||||||
|
ButtonConfig(
|
||||||
|
GamePadButtonInputId.RightStickButton.ordinal,
|
||||||
|
"R3",
|
||||||
|
true,
|
||||||
|
null,
|
||||||
|
"RightStickButton",
|
||||||
|
setOf(),
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
SecondaryDialConfig.RotationProcessor()
|
||||||
|
),
|
||||||
|
// L3 as a separate button
|
||||||
|
SecondaryDialConfig.SingleButton(
|
||||||
|
/* sector */ 6,
|
||||||
|
buttonScale,
|
||||||
|
0.0f,
|
||||||
|
ButtonConfig(
|
||||||
|
GamePadButtonInputId.LeftStickButton.ordinal,
|
||||||
|
"L3",
|
||||||
|
true,
|
||||||
|
null,
|
||||||
|
"LeftStickButton",
|
||||||
|
setOf(),
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
SecondaryDialConfig.RotationProcessor()
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,510 @@
|
|||||||
|
package org.kenjinx.android
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.content.ComponentName
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.ServiceConnection
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.Handler
|
||||||
|
import android.os.IBinder
|
||||||
|
import android.os.Looper
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.SurfaceHolder
|
||||||
|
import android.view.SurfaceView
|
||||||
|
import androidx.compose.runtime.MutableState
|
||||||
|
import org.kenjinx.android.service.EmulationService
|
||||||
|
import org.kenjinx.android.viewmodels.GameModel
|
||||||
|
import org.kenjinx.android.viewmodels.MainViewModel
|
||||||
|
import kotlin.concurrent.thread
|
||||||
|
|
||||||
|
@SuppressLint("ViewConstructor")
|
||||||
|
class GameHost(context: Context?, private val mainViewModel: MainViewModel) : SurfaceView(context),
|
||||||
|
SurfaceHolder.Callback {
|
||||||
|
|
||||||
|
private var isProgressHidden: Boolean = false
|
||||||
|
private var progress: MutableState<String>? = null
|
||||||
|
private var progressValue: MutableState<Float>? = null
|
||||||
|
private var showLoading: MutableState<Boolean>? = null
|
||||||
|
private var game: GameModel? = null
|
||||||
|
|
||||||
|
private var _isClosed: Boolean = false
|
||||||
|
private var _renderingThreadWatcher: Thread? = null
|
||||||
|
private var _height: Int = 0
|
||||||
|
private var _width: Int = 0
|
||||||
|
private var _updateThread: Thread? = null
|
||||||
|
private var _guestThread: Thread? = null
|
||||||
|
private var _isInit: Boolean = false
|
||||||
|
private var _isStarted: Boolean = false
|
||||||
|
private val _nativeWindow: NativeWindow
|
||||||
|
|
||||||
|
private val mainHandler = Handler(Looper.getMainLooper())
|
||||||
|
|
||||||
|
// ---- Foreground service binding ----
|
||||||
|
private var emuBound = false
|
||||||
|
private var emuBinder: EmulationService.LocalBinder? = null
|
||||||
|
private var _startedViaService = false
|
||||||
|
private var _inputInitialized: Boolean = false
|
||||||
|
|
||||||
|
private val emuConn = object : ServiceConnection {
|
||||||
|
override fun onServiceConnected(name: ComponentName, service: IBinder) {
|
||||||
|
emuBinder = service as EmulationService.LocalBinder
|
||||||
|
emuBound = true
|
||||||
|
ghLog("EmulationService bound")
|
||||||
|
|
||||||
|
// If the start has already been prepared and no loop is currently running → start it in the service now
|
||||||
|
if (_isStarted && !_startedViaService && _guestThread == null) {
|
||||||
|
startRunLoopInService()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onServiceDisconnected(name: ComponentName) {
|
||||||
|
ghLog("EmulationService unbound")
|
||||||
|
emuBound = false
|
||||||
|
emuBinder = null
|
||||||
|
_startedViaService = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resize Stabilizer
|
||||||
|
private var stabilizerActive = false
|
||||||
|
|
||||||
|
// last known Android rotation (0,1,2,3)
|
||||||
|
private var lastRotation: Int? = null
|
||||||
|
|
||||||
|
// Debounce for resize kick
|
||||||
|
private var lastKickAt = 0L
|
||||||
|
|
||||||
|
var currentSurface: Long = -1
|
||||||
|
private set
|
||||||
|
|
||||||
|
val currentWindowHandle: Long
|
||||||
|
get() = _nativeWindow.nativePointer
|
||||||
|
|
||||||
|
init {
|
||||||
|
holder.addCallback(this)
|
||||||
|
_nativeWindow = NativeWindow(this)
|
||||||
|
mainViewModel.gameHost = this
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ghLog(msg: String) {
|
||||||
|
val enabled = BuildConfig.DEBUG && org.kenjinx.android.viewmodels.QuickSettings(mainViewModel.activity).enableDebugLogs
|
||||||
|
if (enabled) Log.d("GameHost", msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (Re)bind the current ANativeWindow to the renderer.
|
||||||
|
* Forces a fresh native pointer and passes it to C#.
|
||||||
|
*/
|
||||||
|
fun rebindNativeWindow(force: Boolean = false) {
|
||||||
|
if (_isClosed) return
|
||||||
|
try {
|
||||||
|
currentSurface = _nativeWindow.requeryWindowHandle()
|
||||||
|
_nativeWindow.swapInterval = 0
|
||||||
|
KenjinxNative.deviceSetWindowHandle(currentWindowHandle)
|
||||||
|
|
||||||
|
val w = if (holder.surfaceFrame.width() > 0) holder.surfaceFrame.width() else width
|
||||||
|
val h = if (holder.surfaceFrame.height() > 0) holder.surfaceFrame.height() else height
|
||||||
|
if (w > 0 && h > 0) {
|
||||||
|
if (MainActivity.mainViewModel?.rendererReady == true) {
|
||||||
|
try { KenjinxNative.graphicsRendererSetSize(w, h) } catch (_: Throwable) {}
|
||||||
|
}
|
||||||
|
if (_inputInitialized) {
|
||||||
|
try { KenjinxNative.inputSetClientSize(w, h) } catch (_: Throwable) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (_: Throwable) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* After a successful reattachment, safely "wake up" the swapchain/viewport:
|
||||||
|
* - Set rotation
|
||||||
|
* - Two consecutive resize kicks
|
||||||
|
*/
|
||||||
|
fun postReattachKicks(rotation: Int?) {
|
||||||
|
if (_isClosed) return
|
||||||
|
try {
|
||||||
|
KenjinxNative.setSurfaceRotationByAndroidRotation(rotation ?: 0)
|
||||||
|
val w = if (holder.surfaceFrame.width() > 0) holder.surfaceFrame.width() else width
|
||||||
|
val h = if (holder.surfaceFrame.height() > 0) holder.surfaceFrame.height() else height
|
||||||
|
if (w > 0 && h > 0 &&
|
||||||
|
MainActivity.mainViewModel?.rendererReady == true &&
|
||||||
|
_isStarted && _inputInitialized
|
||||||
|
) {
|
||||||
|
try { KenjinxNative.resizeRendererAndInput(w, h) } catch (_: Throwable) {}
|
||||||
|
mainHandler.postDelayed({
|
||||||
|
try { KenjinxNative.resizeRendererAndInput(w, h) } catch (_: Throwable) {}
|
||||||
|
}, 32)
|
||||||
|
}
|
||||||
|
} catch (_: Throwable) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------- Surface Lifecycle --------
|
||||||
|
|
||||||
|
override fun surfaceCreated(holder: SurfaceHolder) {
|
||||||
|
ghLog("surfaceCreated")
|
||||||
|
// Bind early so that the service is already in place before we start
|
||||||
|
ensureServiceStartedAndBound()
|
||||||
|
rebindNativeWindow(force = true)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
|
||||||
|
ghLog("surfaceChanged ${width}x$height")
|
||||||
|
if (_isClosed) return
|
||||||
|
|
||||||
|
// Always rebind – even if the size remains the same
|
||||||
|
rebindNativeWindow(force = true)
|
||||||
|
|
||||||
|
val sizeChanged = (_width != width || _height != height)
|
||||||
|
_width = width
|
||||||
|
_height = height
|
||||||
|
|
||||||
|
// Ensure service availability & start rendering
|
||||||
|
ensureServiceStartedAndBound()
|
||||||
|
start(holder)
|
||||||
|
|
||||||
|
// Stabilize resizing (adopts a plausible final size setting)
|
||||||
|
startStabilizedResize(expectedRotation = lastRotation)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun surfaceDestroyed(holder: SurfaceHolder) {
|
||||||
|
ghLog("surfaceDestroyed → shutdownBinding()")
|
||||||
|
try { KenjinxNative.detachWindow() } catch (_: Throwable) {}
|
||||||
|
// Always bind and unbind (prevents leaks when swiping away tasks)
|
||||||
|
shutdownBinding()
|
||||||
|
// The actual emulator termination happens via close() / Exit Game
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onWindowVisibilityChanged(visibility: Int) {
|
||||||
|
super.onWindowVisibilityChanged(visibility)
|
||||||
|
if (visibility != VISIBLE) {
|
||||||
|
ghLog("window not visible → shutdownBinding()")
|
||||||
|
try { KenjinxNative.detachWindow() } catch (_: Throwable) {}
|
||||||
|
shutdownBinding()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------- UI Progress --------
|
||||||
|
|
||||||
|
fun setProgress(info: String, progressVal: Float) {
|
||||||
|
showLoading?.apply {
|
||||||
|
progressValue?.apply { this.value = progressVal }
|
||||||
|
progress?.apply { this.value = info }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setProgressStates(
|
||||||
|
showLoading: MutableState<Boolean>?,
|
||||||
|
progressValue: MutableState<Float>?,
|
||||||
|
progress: MutableState<String>?
|
||||||
|
) {
|
||||||
|
this.showLoading = showLoading
|
||||||
|
this.progressValue = progressValue
|
||||||
|
this.progress = progress
|
||||||
|
showLoading?.apply { value = !isProgressHidden }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hideProgressIndicator() {
|
||||||
|
isProgressHidden = true
|
||||||
|
showLoading?.apply {
|
||||||
|
if (value == isProgressHidden) value = !isProgressHidden
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------- Start/Stop Emulation --------
|
||||||
|
|
||||||
|
private fun start(surfaceHolder: SurfaceHolder) {
|
||||||
|
if (_isStarted) return
|
||||||
|
|
||||||
|
// Do NOT set _isStarted = true immediately → first prepare everything
|
||||||
|
rebindNativeWindow(force = true)
|
||||||
|
|
||||||
|
game = if (mainViewModel.isMiiEditorLaunched) null else mainViewModel.gameModel
|
||||||
|
|
||||||
|
// Input initialize
|
||||||
|
KenjinxNative.inputInitialize(width, height)
|
||||||
|
_inputInitialized = true
|
||||||
|
|
||||||
|
val id = mainViewModel.physicalControllerManager?.connect()
|
||||||
|
mainViewModel.motionSensorManager?.setControllerId(id ?: -1)
|
||||||
|
|
||||||
|
val currentRot = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
|
mainViewModel.activity.display?.rotation
|
||||||
|
} else null
|
||||||
|
lastRotation = currentRot
|
||||||
|
|
||||||
|
try {
|
||||||
|
KenjinxNative.setSurfaceRotationByAndroidRotation(currentRot ?: 0)
|
||||||
|
try { KenjinxNative.deviceSetWindowHandle(currentWindowHandle) } catch (_: Throwable) {}
|
||||||
|
|
||||||
|
// Gentle kick only when renderer is READY **and** input is initialized
|
||||||
|
if (width > 0 && height > 0 &&
|
||||||
|
MainActivity.mainViewModel?.rendererReady == true &&
|
||||||
|
_inputInitialized
|
||||||
|
) {
|
||||||
|
try { KenjinxNative.resizeRendererAndInput(width, height) } catch (_: Throwable) {}
|
||||||
|
}
|
||||||
|
} catch (_: Throwable) {}
|
||||||
|
|
||||||
|
val qs = org.kenjinx.android.viewmodels.QuickSettings(mainViewModel.activity)
|
||||||
|
try { KenjinxNative.graphicsSetFullscreenStretch(qs.stretchToFullscreen) } catch (_: Throwable) {}
|
||||||
|
|
||||||
|
// The host is now considered "started".
|
||||||
|
_isStarted = true
|
||||||
|
|
||||||
|
// Always try starting with the service first; if binding is not yet complete → wait briefly, then fall back
|
||||||
|
if (emuBound) {
|
||||||
|
startRunLoopInService()
|
||||||
|
} else {
|
||||||
|
ghLog("Service not yet bound → delayed runloop start")
|
||||||
|
mainHandler.postDelayed({
|
||||||
|
if (!_isStarted) return@postDelayed
|
||||||
|
if (emuBound) {
|
||||||
|
startRunLoopInService()
|
||||||
|
} else {
|
||||||
|
// Fallback: Local thread (this should rarely happen)
|
||||||
|
ghLog("Fallback: starting RunLoop in local thread")
|
||||||
|
_guestThread = thread(start = true, name = "KenjinxGuest") { runGame() }
|
||||||
|
}
|
||||||
|
}, 150)
|
||||||
|
}
|
||||||
|
|
||||||
|
_updateThread = thread(start = true, name = "KenjinxInput/Stats") {
|
||||||
|
var c = 0
|
||||||
|
while (_isStarted) {
|
||||||
|
KenjinxNative.inputUpdate()
|
||||||
|
Thread.sleep(1)
|
||||||
|
if (++c >= 1000) {
|
||||||
|
if (progressValue?.value == -1f) {
|
||||||
|
progress?.apply {
|
||||||
|
this.value = "Loading ${if (mainViewModel.isMiiEditorLaunched) "Mii Editor" else game?.titleName ?: ""}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c = 0
|
||||||
|
mainViewModel.updateStats(
|
||||||
|
KenjinxNative.deviceGetGameFifo(),
|
||||||
|
KenjinxNative.deviceGetGameFrameRate(),
|
||||||
|
KenjinxNative.deviceGetGameFrameTime()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun runGame() {
|
||||||
|
mainViewModel.onEmulationStarted()
|
||||||
|
try {
|
||||||
|
KenjinxNative.graphicsRendererRunLoop()
|
||||||
|
} finally {
|
||||||
|
mainViewModel.onEmulationStopped()
|
||||||
|
}
|
||||||
|
game?.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun close() {
|
||||||
|
ghLog("close()")
|
||||||
|
_isClosed = true
|
||||||
|
_isInit = false
|
||||||
|
_isStarted = false
|
||||||
|
_inputInitialized = false
|
||||||
|
|
||||||
|
KenjinxNative.uiHandlerSetResponse(false, "")
|
||||||
|
|
||||||
|
// Stop emulation in the service (if it was started there)
|
||||||
|
try {
|
||||||
|
if (emuBound && _startedViaService) {
|
||||||
|
emuBinder?.stopEmulation {
|
||||||
|
// Close is handled in the service hard-close path.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (_: Throwable) { }
|
||||||
|
|
||||||
|
// Fallback: Terminate local thread
|
||||||
|
try { _updateThread?.join(200) } catch (_: Throwable) {}
|
||||||
|
try { _renderingThreadWatcher?.join(200) } catch (_: Throwable) {}
|
||||||
|
try { _guestThread?.join(1000) } catch (_: Throwable) {}
|
||||||
|
|
||||||
|
if (!(emuBound && _startedViaService)) {
|
||||||
|
try { KenjinxNative.deviceCloseEmulation() } catch (_: Throwable) {}
|
||||||
|
mainViewModel.onEmulationStopped()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Release the bond
|
||||||
|
shutdownBinding()
|
||||||
|
|
||||||
|
// Explicitly terminate the service (if it is still running)
|
||||||
|
try {
|
||||||
|
mainViewModel.activity.stopService(Intent(mainViewModel.activity, EmulationService::class.java))
|
||||||
|
} catch (_: Throwable) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------- Orientation / Resize --------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Securely setting the renderer/input size
|
||||||
|
*/
|
||||||
|
@Synchronized
|
||||||
|
private fun safeSetSize(w: Int, h: Int) {
|
||||||
|
if (_isClosed || w <= 0 || h <= 0) return
|
||||||
|
try {
|
||||||
|
ghLog("safeSetSize: ${w}x$h (started=$_isStarted, inputInit=$_inputInitialized)")
|
||||||
|
KenjinxNative.graphicsRendererSetSize(w, h)
|
||||||
|
if (_isStarted && _inputInitialized) {
|
||||||
|
KenjinxNative.inputSetClientSize(w, h)
|
||||||
|
}
|
||||||
|
} catch (t: Throwable) {
|
||||||
|
Log.e("GameHost", "safeSetSize failed: ${t.message}", t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by the activity when the rotation/layout changes.
|
||||||
|
* Detects 90°↔270° and forces (debounces) a requery/resize.
|
||||||
|
*/
|
||||||
|
fun onOrientationOrSizeChanged(rotation: Int? = null) {
|
||||||
|
if (_isClosed) return
|
||||||
|
|
||||||
|
val old = lastRotation
|
||||||
|
lastRotation = rotation
|
||||||
|
|
||||||
|
val isSideFlip = (old == 1 && rotation == 3) || (old == 3 && rotation == 1)
|
||||||
|
|
||||||
|
if (isSideFlip) {
|
||||||
|
try { KenjinxNative.setSurfaceRotationByAndroidRotation(rotation) } catch (_: Throwable) {}
|
||||||
|
rebindNativeWindow(force = true)
|
||||||
|
val now = android.os.SystemClock.uptimeMillis()
|
||||||
|
if (now - lastKickAt >= 300L && _inputInitialized && MainActivity.mainViewModel?.rendererReady == true) {
|
||||||
|
lastKickAt = now
|
||||||
|
val w = if (holder.surfaceFrame.width() > 0) holder.surfaceFrame.width() else width
|
||||||
|
val h = if (holder.surfaceFrame.height() > 0) holder.surfaceFrame.height() else height
|
||||||
|
if (w > 0 && h > 0) {
|
||||||
|
try { KenjinxNative.resizeRendererAndInput(w, h) } catch (_: Throwable) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
startStabilizedResize(rotation)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait a moment until the surface has its final dimensions after rotation,
|
||||||
|
* checks plausibility (portrait/landscape) and only then sets the size.
|
||||||
|
*/
|
||||||
|
private fun startStabilizedResize(expectedRotation: Int?) {
|
||||||
|
if (_isClosed) return
|
||||||
|
|
||||||
|
// Restart if already active
|
||||||
|
if (stabilizerActive) {
|
||||||
|
stabilizerActive = false
|
||||||
|
}
|
||||||
|
stabilizerActive = true
|
||||||
|
|
||||||
|
var attempts = 0
|
||||||
|
var stableCount = 0
|
||||||
|
var lastW = -1
|
||||||
|
var lastH = -1
|
||||||
|
|
||||||
|
val task = object : Runnable {
|
||||||
|
override fun run() {
|
||||||
|
if (!_isStarted || _isClosed) {
|
||||||
|
stabilizerActive = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prefer real frame size
|
||||||
|
var w = holder.surfaceFrame.width()
|
||||||
|
var h = holder.surfaceFrame.height()
|
||||||
|
|
||||||
|
// Fallbacks
|
||||||
|
if (w <= 0 || h <= 0) {
|
||||||
|
w = width
|
||||||
|
h = height
|
||||||
|
}
|
||||||
|
|
||||||
|
// If rotation is known: Force plausibility (Landscape ↔ Portrait)
|
||||||
|
expectedRotation?.let { rot ->
|
||||||
|
val landscape = (rot == 1 || rot == 3) // ROTATION_90/270
|
||||||
|
if (landscape && h > w) {
|
||||||
|
val t = w; w = h; h = t
|
||||||
|
} else if (!landscape && w > h) {
|
||||||
|
val t = w; w = h; h = t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stability test
|
||||||
|
if (w == lastW && h == lastH && w > 0 && h > 0) {
|
||||||
|
stableCount++
|
||||||
|
} else {
|
||||||
|
stableCount = 0
|
||||||
|
lastW = w
|
||||||
|
lastH = h
|
||||||
|
}
|
||||||
|
|
||||||
|
attempts++
|
||||||
|
|
||||||
|
// 1 stable tick or a maximum of 12 attempts
|
||||||
|
if ((stableCount >= 1 || attempts >= 12) && w > 0 && h > 0) {
|
||||||
|
ghLog("resize stabilized after $attempts ticks → ${w}x$h")
|
||||||
|
safeSetSize(w, h)
|
||||||
|
stabilizerActive = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stabilizerActive) {
|
||||||
|
mainHandler.postDelayed(this, 16)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mainHandler.post(task)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== Service helpers =====
|
||||||
|
|
||||||
|
/** Callable from Activity/Surface lifecycle to ensure FGS is running */
|
||||||
|
fun ensureServiceStartedAndBound() {
|
||||||
|
val act = mainViewModel.activity
|
||||||
|
val intent = Intent(act, EmulationService::class.java)
|
||||||
|
try {
|
||||||
|
act.startForegroundService(intent)
|
||||||
|
} catch (_: Throwable) { }
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!emuBound) {
|
||||||
|
act.bindService(intent, emuConn, Context.BIND_AUTO_CREATE)
|
||||||
|
}
|
||||||
|
} catch (_: Throwable) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Call this from the Activity in onPause/onStop/onDestroy (and internally in surfaceDestroyed/onWindowVisibilityChanged) */
|
||||||
|
fun shutdownBinding() {
|
||||||
|
if (emuBound) {
|
||||||
|
try {
|
||||||
|
mainViewModel.activity.unbindService(emuConn)
|
||||||
|
} catch (_: Throwable) { }
|
||||||
|
emuBound = false
|
||||||
|
emuBinder = null
|
||||||
|
_startedViaService = false
|
||||||
|
ghLog("shutdownBinding() → unbound")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun startRunLoopInService() {
|
||||||
|
if (!emuBound) return
|
||||||
|
if (_startedViaService) return
|
||||||
|
_startedViaService = true
|
||||||
|
|
||||||
|
emuBinder?.startEmulation {
|
||||||
|
try {
|
||||||
|
mainViewModel.onEmulationStarted()
|
||||||
|
KenjinxNative.graphicsRendererRunLoop()
|
||||||
|
} catch (t: Throwable) {
|
||||||
|
Log.e("GameHost", "RunLoop crash in service", t)
|
||||||
|
} finally {
|
||||||
|
_startedViaService = false
|
||||||
|
mainViewModel.onEmulationStopped()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ghLog("RunLoop started in EmulationService")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package org.kenjinx.android
|
||||||
|
|
||||||
|
enum class GamePadButtonInputId {
|
||||||
|
None,
|
||||||
|
|
||||||
|
// Buttons
|
||||||
|
A,
|
||||||
|
B,
|
||||||
|
X,
|
||||||
|
Y,
|
||||||
|
LeftStickButton,
|
||||||
|
RightStickButton,
|
||||||
|
LeftShoulder,
|
||||||
|
RightShoulder,
|
||||||
|
LeftTrigger,
|
||||||
|
RightTrigger,
|
||||||
|
DpadUp,
|
||||||
|
DpadDown,
|
||||||
|
DpadLeft,
|
||||||
|
DpadRight,
|
||||||
|
Minus,
|
||||||
|
Plus,
|
||||||
|
|
||||||
|
// Stick
|
||||||
|
LeftStick,
|
||||||
|
RightStick
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package org.kenjinx.android
|
||||||
|
|
||||||
|
interface IGameController {
|
||||||
|
val isVisible: Boolean
|
||||||
|
fun setVisible(isVisible: Boolean)
|
||||||
|
fun connect()
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,20 @@
|
|||||||
|
package org.kenjinx.android
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import android.content.Context
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
class KenjinxApplication : Application() {
|
||||||
|
init {
|
||||||
|
instance = this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getPublicFilesDir(): File = getExternalFilesDir(null) ?: filesDir
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
lateinit var instance: KenjinxApplication
|
||||||
|
private set
|
||||||
|
|
||||||
|
val context: Context get() = instance.applicationContext
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,371 @@
|
|||||||
|
package org.kenjinx.android
|
||||||
|
|
||||||
|
import android.os.Build
|
||||||
|
import com.sun.jna.JNIEnv
|
||||||
|
import com.sun.jna.Library
|
||||||
|
import com.sun.jna.Native
|
||||||
|
import org.kenjinx.android.viewmodels.GameInfo
|
||||||
|
import java.util.Collections
|
||||||
|
import android.view.Surface
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.annotation.RequiresApi
|
||||||
|
|
||||||
|
interface KenjinxNativeJna : Library {
|
||||||
|
fun deviceInitialize(
|
||||||
|
memoryManagerMode: Int,
|
||||||
|
useNce: Boolean,
|
||||||
|
memoryConfiguration: Int,
|
||||||
|
systemLanguage: Int,
|
||||||
|
regionCode: Int,
|
||||||
|
vSyncMode: Int,
|
||||||
|
enableDockedMode: Boolean,
|
||||||
|
enablePptc: Boolean,
|
||||||
|
enableLowPowerPptc: Boolean,
|
||||||
|
enableJitCacheEviction: Boolean,
|
||||||
|
enableInternetAccess: Boolean,
|
||||||
|
enableFsIntegrityChecks: Boolean,
|
||||||
|
fsGlobalAccessLogMode: Int,
|
||||||
|
timeZone: String,
|
||||||
|
ignoreMissingServices: Boolean
|
||||||
|
): Boolean
|
||||||
|
|
||||||
|
fun graphicsInitialize(
|
||||||
|
rescale: Float = 1f,
|
||||||
|
maxAnisotropy: Float = 0f,
|
||||||
|
fastGpuTime: Boolean = true,
|
||||||
|
fast2DCopy: Boolean = true,
|
||||||
|
enableMacroJit: Boolean = false,
|
||||||
|
enableMacroHLE: Boolean = true,
|
||||||
|
enableShaderCache: Boolean = true,
|
||||||
|
enableTextureRecompression: Boolean = false,
|
||||||
|
backendThreading: Int = BackendThreading.Auto.ordinal
|
||||||
|
): Boolean
|
||||||
|
|
||||||
|
fun graphicsInitializeRenderer(
|
||||||
|
extensions: Array<String>,
|
||||||
|
extensionsLength: Int,
|
||||||
|
driver: Long
|
||||||
|
): Boolean
|
||||||
|
|
||||||
|
fun javaInitialize(appPath: String, env: JNIEnv): Boolean
|
||||||
|
fun deviceLaunchMiiEditor(): Boolean
|
||||||
|
fun deviceGetGameFrameRate(): Double
|
||||||
|
fun deviceGetGameFrameTime(): Double
|
||||||
|
fun deviceGetGameFifo(): Double
|
||||||
|
fun deviceLoadDescriptor(fileDescriptor: Int, gameType: Int, updateDescriptor: Int): Boolean
|
||||||
|
fun graphicsRendererSetSize(width: Int, height: Int)
|
||||||
|
fun graphicsRendererSetVsync(vSyncMode: Int)
|
||||||
|
fun graphicsRendererRunLoop()
|
||||||
|
fun graphicsSetFullscreenStretch(enable: Boolean)
|
||||||
|
fun deviceReloadFilesystem()
|
||||||
|
fun inputInitialize(width: Int, height: Int)
|
||||||
|
fun inputSetClientSize(width: Int, height: Int)
|
||||||
|
fun inputSetTouchPoint(x: Int, y: Int)
|
||||||
|
fun inputReleaseTouchPoint()
|
||||||
|
fun inputUpdate()
|
||||||
|
fun inputSetButtonPressed(button: Int, id: Int)
|
||||||
|
fun inputSetButtonReleased(button: Int, id: Int)
|
||||||
|
fun inputConnectGamepad(index: Int): Int
|
||||||
|
fun inputSetStickAxis(stick: Int, x: Float, y: Float, id: Int)
|
||||||
|
fun inputSetAccelerometerData(x: Float, y: Float, z: Float, id: Int)
|
||||||
|
fun inputSetGyroData(x: Float, y: Float, z: Float, id: Int)
|
||||||
|
fun deviceCloseEmulation()
|
||||||
|
fun deviceReinitEmulation()
|
||||||
|
fun deviceSignalEmulationClose()
|
||||||
|
|
||||||
|
fun detachWindow()
|
||||||
|
// >>> Rendering-related additions for the toggle:
|
||||||
|
fun deviceWaitForGpuDone(timeoutMs: Int)
|
||||||
|
fun deviceRecreateSwapchain()
|
||||||
|
fun graphicsSetBackendThreading(mode: Int)
|
||||||
|
fun graphicsSetPresentEnabled(enabled: Boolean)
|
||||||
|
// <<<
|
||||||
|
|
||||||
|
fun userGetOpenedUser(): String
|
||||||
|
fun userGetUserPicture(userId: String): String
|
||||||
|
fun userSetUserPicture(userId: String, picture: String)
|
||||||
|
fun userGetUserName(userId: String): String
|
||||||
|
fun userSetUserName(userId: String, userName: String)
|
||||||
|
fun userAddUser(username: String, picture: String)
|
||||||
|
fun userDeleteUser(userId: String)
|
||||||
|
fun userOpenUser(userId: String)
|
||||||
|
fun userCloseUser(userId: String)
|
||||||
|
fun loggingSetEnabled(logLevel: Int, enabled: Boolean)
|
||||||
|
fun deviceVerifyFirmware(fileDescriptor: Int, isXci: Boolean): String
|
||||||
|
fun deviceInstallFirmware(fileDescriptor: Int, isXci: Boolean)
|
||||||
|
fun deviceGetInstalledFirmwareVersion(): String
|
||||||
|
fun uiHandlerSetup()
|
||||||
|
fun uiHandlerSetResponse(isOkPressed: Boolean, input: String)
|
||||||
|
fun deviceGetDlcTitleId(path: String, ncaPath: String): String
|
||||||
|
fun deviceGetGameInfo(fileDescriptor: Int, extension: String, info: GameInfo)
|
||||||
|
fun userGetAllUsers(): Array<String>
|
||||||
|
fun deviceGetDlcContentList(path: String, titleId: Long): Array<String>
|
||||||
|
fun loggingEnabledGraphicsLog(enabled: Boolean)
|
||||||
|
// Surface rotation (0/90/180/270 degrees)
|
||||||
|
fun deviceSetSurfaceRotation(degrees: Int)
|
||||||
|
// (optional alias): compact resize shortcut
|
||||||
|
fun deviceResize(width: Int, height: Int)
|
||||||
|
// Set window handle after each query
|
||||||
|
fun deviceSetWindowHandle(handle: Long)
|
||||||
|
// Amiibo
|
||||||
|
fun amiiboLoadBin(bytes: ByteArray, length: Int): Boolean
|
||||||
|
fun amiiboClear()
|
||||||
|
|
||||||
|
// AUDIO: Direct JNA bridge to C# exports
|
||||||
|
fun audioSetPaused(paused: Boolean)
|
||||||
|
fun audioSetMuted(muted: Boolean)
|
||||||
|
}
|
||||||
|
|
||||||
|
val jnaInstance: KenjinxNativeJna = Native.load(
|
||||||
|
"kenjinx",
|
||||||
|
KenjinxNativeJna::class.java,
|
||||||
|
Collections.singletonMap(Library.OPTION_ALLOW_OBJECTS, true)
|
||||||
|
)
|
||||||
|
|
||||||
|
object KenjinxNative : KenjinxNativeJna by jnaInstance {
|
||||||
|
|
||||||
|
fun loggingSetEnabled(logLevel: LogLevel, enabled: Boolean) =
|
||||||
|
loggingSetEnabled(logLevel.ordinal, enabled)
|
||||||
|
|
||||||
|
// --- Rendering: Single-threaded option & secure wrappers --------------------
|
||||||
|
|
||||||
|
// 0 = Auto, 1 = SingleThread (Disable Threaded), 2 = Threaded
|
||||||
|
private const val THREADING_AUTO = 0
|
||||||
|
private const val THREADING_SINGLE = 1
|
||||||
|
private const val THREADING_THREADED = 2
|
||||||
|
|
||||||
|
override fun graphicsSetBackendThreading(mode: Int) {
|
||||||
|
try {
|
||||||
|
jnaInstance.graphicsSetBackendThreading(mode)
|
||||||
|
} catch (_: Throwable) {
|
||||||
|
Log.w("KenjinxNative", "graphicsSetBackendThreading not available")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun deviceRecreateSwapchain() {
|
||||||
|
try { jnaInstance.deviceRecreateSwapchain() } catch (_: Throwable) { /* ignore */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun deviceWaitForGpuDone(timeoutMs: Int) {
|
||||||
|
try { jnaInstance.deviceWaitForGpuDone(timeoutMs) } catch (_: Throwable) { /* ignore */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun graphicsSetPresentEnabled(enabled: Boolean) {
|
||||||
|
try { jnaInstance.graphicsSetPresentEnabled(enabled) } catch (_: Throwable) { /* ignore */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Secure deviceResize implementation (pure rendering)
|
||||||
|
override fun deviceResize(width: Int, height: Int) {
|
||||||
|
try {
|
||||||
|
graphicsRendererSetSize(width, height)
|
||||||
|
inputSetClientSize(width, height)
|
||||||
|
} catch (_: Throwable) { /* ignore */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Robust graphicsInitialize with QCOM heuristics + fallback → SingleThread
|
||||||
|
override fun graphicsInitialize(
|
||||||
|
rescale: Float,
|
||||||
|
maxAnisotropy: Float,
|
||||||
|
fastGpuTime: Boolean,
|
||||||
|
fast2DCopy: Boolean,
|
||||||
|
enableMacroJit: Boolean,
|
||||||
|
enableMacroHLE: Boolean,
|
||||||
|
enableShaderCache: Boolean,
|
||||||
|
enableTextureRecompression: Boolean,
|
||||||
|
backendThreading: Int
|
||||||
|
): Boolean {
|
||||||
|
val requested = backendThreading
|
||||||
|
val isQcom = "qcom".equals(Build.HARDWARE, true)
|
||||||
|
|
||||||
|
// Heuristic: On QCOM, when "Auto" is selected, first try single-threaded mode;
|
||||||
|
// explicitly set values remain unchanged.
|
||||||
|
val firstChoice =
|
||||||
|
if (isQcom && requested == THREADING_AUTO) THREADING_SINGLE else requested
|
||||||
|
|
||||||
|
Log.i(
|
||||||
|
"KenjinxNative",
|
||||||
|
"graphicsInitialize: request=$requested firstChoice=$firstChoice hw=${Build.HARDWARE}"
|
||||||
|
)
|
||||||
|
|
||||||
|
return try {
|
||||||
|
jnaInstance.graphicsInitialize(
|
||||||
|
rescale,
|
||||||
|
maxAnisotropy,
|
||||||
|
fastGpuTime,
|
||||||
|
fast2DCopy,
|
||||||
|
enableMacroJit,
|
||||||
|
enableMacroHLE,
|
||||||
|
enableShaderCache,
|
||||||
|
enableTextureRecompression,
|
||||||
|
firstChoice
|
||||||
|
)
|
||||||
|
} catch (t: Throwable) {
|
||||||
|
Log.e(
|
||||||
|
"KenjinxNative",
|
||||||
|
"graphicsInitialize failed (firstChoice=$firstChoice). Fallback → SingleThread",
|
||||||
|
t
|
||||||
|
)
|
||||||
|
try {
|
||||||
|
jnaInstance.graphicsInitialize(
|
||||||
|
rescale,
|
||||||
|
maxAnisotropy,
|
||||||
|
fastGpuTime,
|
||||||
|
fast2DCopy,
|
||||||
|
enableMacroJit,
|
||||||
|
enableMacroHLE,
|
||||||
|
enableShaderCache,
|
||||||
|
enableTextureRecompression,
|
||||||
|
THREADING_SINGLE
|
||||||
|
)
|
||||||
|
} catch (t2: Throwable) {
|
||||||
|
Log.e("KenjinxNative", "graphicsInitialize fallback failed", t2)
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Optional wrappers for audio (safer logging) ---
|
||||||
|
override fun audioSetPaused(paused: Boolean) {
|
||||||
|
try { jnaInstance.audioSetPaused(paused) }
|
||||||
|
catch (t: Throwable) { Log.w("KenjinxNative", "audioSetPaused unavailable", t) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun audioSetMuted(muted: Boolean) {
|
||||||
|
try { jnaInstance.audioSetMuted(muted) }
|
||||||
|
catch (t: Throwable) { Log.w("KenjinxNative", "audioSetMuted unavailable", t) }
|
||||||
|
}
|
||||||
|
// ----------------------------------------------------
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.R)
|
||||||
|
@JvmStatic
|
||||||
|
fun frameEnded() = MainActivity.frameEnded()
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun test() { /* no-op */ }
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun getSurfacePtr(): Long = MainActivity.mainViewModel?.gameHost?.currentSurface ?: -1
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun getWindowHandle(): Long =
|
||||||
|
MainActivity.mainViewModel?.gameHost?.currentWindowHandle ?: -1
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun updateProgress(infoPtr: Long, progress: Float) {
|
||||||
|
// Get string from native pointer and push into progress overlay
|
||||||
|
val text = NativeHelpers.instance.getStringJava(infoPtr)
|
||||||
|
MainActivity.mainViewModel?.gameHost?.setProgress(text, progress)
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun onSurfaceSizeChanged(width: Int, height: Int) {
|
||||||
|
// No-Op: Placeholder – Hook if needed.
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun setSurfaceRotationByAndroidRotation(androidRotation: Int?) {
|
||||||
|
val degrees = when (androidRotation) {
|
||||||
|
Surface.ROTATION_0 -> 0
|
||||||
|
Surface.ROTATION_90 -> 90
|
||||||
|
Surface.ROTATION_180 -> 180
|
||||||
|
Surface.ROTATION_270 -> 270
|
||||||
|
else -> 0
|
||||||
|
}
|
||||||
|
try { deviceSetSurfaceRotation(degrees) } catch (_: Throwable) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun resizeRendererAndInput(width: Int, height: Int) {
|
||||||
|
try {
|
||||||
|
// Alternatively: deviceResize(width, height)
|
||||||
|
graphicsRendererSetSize(width, height)
|
||||||
|
inputSetClientSize(width, height)
|
||||||
|
} catch (_: Throwable) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun detachWindow() {
|
||||||
|
try { graphicsSetPresentEnabled(false) } catch (_: Throwable) {}
|
||||||
|
try { deviceWaitForGpuDone(100) } catch (_: Throwable) {}
|
||||||
|
try { jnaInstance.detachWindow() } catch (_: Throwable) {}
|
||||||
|
try { deviceSetWindowHandle(0) } catch (_: Throwable) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun reattachWindowIfReady(): Boolean {
|
||||||
|
return try {
|
||||||
|
val handle = getWindowHandle()
|
||||||
|
if (handle <= 0) return false
|
||||||
|
deviceSetWindowHandle(handle)
|
||||||
|
deviceRecreateSwapchain()
|
||||||
|
true
|
||||||
|
} catch (_: Throwable) {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Variant A (Pointer → Strings via NativeHelpers).
|
||||||
|
* Used by older JNI/Interop paths.
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun updateUiHandler(
|
||||||
|
newTitlePointer: Long,
|
||||||
|
newMessagePointer: Long,
|
||||||
|
newWatermarkPointer: Long,
|
||||||
|
newType: Int,
|
||||||
|
min: Int,
|
||||||
|
max: Int,
|
||||||
|
nMode: Int,
|
||||||
|
newSubtitlePointer: Long,
|
||||||
|
newInitialTextPointer: Long
|
||||||
|
) {
|
||||||
|
val title = NativeHelpers.instance.getStringJava(newTitlePointer)
|
||||||
|
val message = NativeHelpers.instance.getStringJava(newMessagePointer)
|
||||||
|
val watermark = NativeHelpers.instance.getStringJava(newWatermarkPointer)
|
||||||
|
val subtitle = NativeHelpers.instance.getStringJava(newSubtitlePointer)
|
||||||
|
val initialText = NativeHelpers.instance.getStringJava(newInitialTextPointer)
|
||||||
|
val mode = KeyboardMode.entries.getOrNull(nMode) ?: KeyboardMode.Default
|
||||||
|
|
||||||
|
MainActivity.mainViewModel?.activity?.uiHandler?.update(
|
||||||
|
newTitle = title,
|
||||||
|
newMessage = message,
|
||||||
|
newWatermark = watermark,
|
||||||
|
newType = newType,
|
||||||
|
min = min,
|
||||||
|
max = max,
|
||||||
|
newMode = mode,
|
||||||
|
newSubtitle = subtitle,
|
||||||
|
newInitialText = initialText
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Variant B (strings directly). Used by newer JNI/interop paths.
|
||||||
|
* Signature exactly matches the C# call in AndroidUIHandler.cs / Interop.UpdateUiHandler(...).
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun uiHandlerUpdate(
|
||||||
|
title: String,
|
||||||
|
message: String,
|
||||||
|
watermark: String,
|
||||||
|
type: Int,
|
||||||
|
min: Int,
|
||||||
|
max: Int,
|
||||||
|
nMode: Int,
|
||||||
|
subtitle: String,
|
||||||
|
initialText: String
|
||||||
|
) {
|
||||||
|
val mode = KeyboardMode.entries.getOrNull(nMode) ?: KeyboardMode.Default
|
||||||
|
MainActivity.mainViewModel?.activity?.uiHandler?.update(
|
||||||
|
newTitle = title,
|
||||||
|
newMessage = message,
|
||||||
|
newWatermark = watermark,
|
||||||
|
newType = type,
|
||||||
|
min = min,
|
||||||
|
max = max,
|
||||||
|
newMode = mode,
|
||||||
|
newSubtitle = subtitle,
|
||||||
|
newInitialText = initialText
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
package org.kenjinx.android
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import androidx.core.content.FileProvider
|
||||||
|
import net.lingala.zip4j.ZipFile
|
||||||
|
import org.kenjinx.android.viewmodels.MainViewModel
|
||||||
|
import java.io.File
|
||||||
|
import java.net.URLConnection
|
||||||
|
|
||||||
|
class Logging(private var viewModel: MainViewModel) {
|
||||||
|
val logPath = MainActivity.AppPath + "/Logs"
|
||||||
|
|
||||||
|
init {
|
||||||
|
File(logPath).mkdirs()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun requestExport() {
|
||||||
|
val files = File(logPath).listFiles()
|
||||||
|
files?.apply {
|
||||||
|
val zipExportPath = MainActivity.AppPath + "/log.zip"
|
||||||
|
File(zipExportPath).delete()
|
||||||
|
var count = 0
|
||||||
|
if (files.isNotEmpty()) {
|
||||||
|
val zipFile = ZipFile(zipExportPath)
|
||||||
|
for (file in files) {
|
||||||
|
if (file.isFile) {
|
||||||
|
zipFile.addFile(file)
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
zipFile.close()
|
||||||
|
}
|
||||||
|
if (count > 0) {
|
||||||
|
val zip = File(zipExportPath)
|
||||||
|
val uri = FileProvider.getUriForFile(
|
||||||
|
viewModel.activity,
|
||||||
|
viewModel.activity.packageName + ".fileprovider",
|
||||||
|
zip
|
||||||
|
)
|
||||||
|
val intent = Intent(Intent.ACTION_SEND)
|
||||||
|
intent.putExtra(Intent.EXTRA_STREAM, uri)
|
||||||
|
intent.setDataAndType(uri, URLConnection.guessContentTypeFromName(zip.name))
|
||||||
|
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||||
|
val chooser = Intent.createChooser(intent, "Share logs")
|
||||||
|
viewModel.activity.startActivity(chooser)
|
||||||
|
} else {
|
||||||
|
File(zipExportPath).delete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun clearLogs() {
|
||||||
|
if (File(logPath).exists()) {
|
||||||
|
File(logPath).deleteRecursively()
|
||||||
|
}
|
||||||
|
|
||||||
|
File(logPath).mkdirs()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class LogLevel {
|
||||||
|
Debug, Stub, Info, Warning, Error, Guest, AccessLog, Notice, Trace
|
||||||
|
}
|
||||||
@@ -0,0 +1,734 @@
|
|||||||
|
package org.kenjinx.android
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.os.Environment
|
||||||
|
import android.view.KeyEvent
|
||||||
|
import android.view.MotionEvent
|
||||||
|
import android.view.WindowManager
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Handler
|
||||||
|
import android.os.Looper
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.activity.compose.setContent
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Surface
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.core.net.toUri
|
||||||
|
import androidx.core.view.WindowCompat
|
||||||
|
import androidx.core.view.WindowInsetsCompat
|
||||||
|
import androidx.core.view.WindowInsetsControllerCompat
|
||||||
|
import androidx.documentfile.provider.DocumentFile
|
||||||
|
import com.anggrayudi.storage.SimpleStorageHelper
|
||||||
|
import com.sun.jna.JNIEnv
|
||||||
|
import org.kenjinx.android.ui.theme.KenjinxAndroidTheme
|
||||||
|
import org.kenjinx.android.viewmodels.MainViewModel
|
||||||
|
import org.kenjinx.android.viewmodels.QuickSettings
|
||||||
|
import org.kenjinx.android.viewmodels.GameModel
|
||||||
|
import org.kenjinx.android.views.MainView
|
||||||
|
import android.content.pm.ActivityInfo
|
||||||
|
import android.content.res.Configuration
|
||||||
|
import android.hardware.display.DisplayManager
|
||||||
|
import android.net.Uri
|
||||||
|
import android.view.Surface
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
|
import java.io.File
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
|
import android.Manifest
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.lifecycle.Lifecycle
|
||||||
|
import android.content.BroadcastReceiver
|
||||||
|
import android.content.IntentFilter
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Build
|
||||||
|
import androidx.annotation.RequiresApi
|
||||||
|
import org.kenjinx.android.service.EmulationService
|
||||||
|
import androidx.core.content.edit
|
||||||
|
|
||||||
|
class MainActivity : BaseActivity() {
|
||||||
|
private var physicalControllerManager: PhysicalControllerManager =
|
||||||
|
PhysicalControllerManager(this)
|
||||||
|
private lateinit var motionSensorManager: MotionSensorManager
|
||||||
|
private var _isInit: Boolean = false
|
||||||
|
private val handler = Handler(Looper.getMainLooper())
|
||||||
|
private val ENABLE_PRESENT_DELAY_MS = 400L
|
||||||
|
private val REATTACH_DELAY_MS = 300L
|
||||||
|
private var wantPresentEnabled = false
|
||||||
|
private val TAG_FG = "FgPresent"
|
||||||
|
private val delayedHandleIntent = object : Runnable { override fun run() { handleIntent() } }
|
||||||
|
var storedIntent: Intent = Intent()
|
||||||
|
var isGameRunning = false
|
||||||
|
var isActive = false
|
||||||
|
var storageHelper: SimpleStorageHelper? = null
|
||||||
|
lateinit var uiHandler: UiHandler
|
||||||
|
|
||||||
|
// Persistence for zombie detection
|
||||||
|
private val PREFS = "emu_core"
|
||||||
|
private val KEY_EMU_RUNNING = "emu_running"
|
||||||
|
|
||||||
|
// Display Rotation + Orientation Handling
|
||||||
|
private lateinit var displayManager: DisplayManager
|
||||||
|
private var lastKnownRotation: Int? = null
|
||||||
|
private var pulsingOrientation = false
|
||||||
|
private var lastPulseAt = 0L
|
||||||
|
|
||||||
|
private val TAG_ROT = "RotationDebug"
|
||||||
|
|
||||||
|
private fun rotLog(msg: String) {
|
||||||
|
val enabled = BuildConfig.DEBUG && QuickSettings(this).enableDebugLogs
|
||||||
|
if (enabled) Log.d(TAG_ROT, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val serviceStopReceiver = object : BroadcastReceiver() {
|
||||||
|
override fun onReceive(context: Context?, intent: Intent?) {
|
||||||
|
if (intent?.action == EmulationService.ACTION_STOPPED) {
|
||||||
|
handler.removeCallbacks(reattachWindowWhenReady)
|
||||||
|
handler.removeCallbacks(enablePresentWhenReady)
|
||||||
|
clearEmuRunningFlag()
|
||||||
|
hardColdReset("service stopped broadcast")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val displayListener = object : DisplayManager.DisplayListener {
|
||||||
|
override fun onDisplayAdded(displayId: Int) {}
|
||||||
|
override fun onDisplayRemoved(displayId: Int) {}
|
||||||
|
override fun onDisplayChanged(displayId: Int) {
|
||||||
|
if (if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
|
display?.displayId != displayId
|
||||||
|
} else {
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
) return
|
||||||
|
val rot = display?.rotation
|
||||||
|
if (rot == lastKnownRotation) return
|
||||||
|
|
||||||
|
rotLog("onDisplayChanged: display.rotation=$rot → ${deg(rot)}°")
|
||||||
|
|
||||||
|
val pref = QuickSettings(this@MainActivity).orientationPreference
|
||||||
|
val old = lastKnownRotation
|
||||||
|
lastKnownRotation = rot
|
||||||
|
|
||||||
|
// 1) Inform Native/Renderer (applies to Sensor & SensorLandscape)
|
||||||
|
try { KenjinxNative.setSurfaceRotationByAndroidRotation(rot) } catch (_: Throwable) {}
|
||||||
|
|
||||||
|
// 2) Initiate host resize (applies to Sensor & SensorLandscape)
|
||||||
|
if (isGameRunning) {
|
||||||
|
handler.post {
|
||||||
|
try { mainViewModel?.gameHost?.onOrientationOrSizeChanged(rot) } catch (_: Throwable) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3) Only with SENSOR_LANDSCAPE: gentle pulse with real 90↔270 flip
|
||||||
|
if (pref == QuickSettings.OrientationPreference.SensorLandscape && old != null && rot != null) {
|
||||||
|
val isSideFlip = (old == Surface.ROTATION_90 && rot == Surface.ROTATION_270) ||
|
||||||
|
(old == Surface.ROTATION_270 && rot == Surface.ROTATION_90)
|
||||||
|
if (isSideFlip) doOrientationPulse(rot)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun deg(r: Int?): Int = when (r) {
|
||||||
|
Surface.ROTATION_0 -> 0
|
||||||
|
Surface.ROTATION_90 -> 90
|
||||||
|
Surface.ROTATION_180 -> 180
|
||||||
|
Surface.ROTATION_270 -> 270
|
||||||
|
else -> -1
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setPresentEnabled(enabled: Boolean, reason: String) {
|
||||||
|
wantPresentEnabled = enabled
|
||||||
|
try {
|
||||||
|
KenjinxNative.graphicsSetPresentEnabled(enabled)
|
||||||
|
Log.d(TAG_FG, "present=${if (enabled) "ENABLED" else "DISABLED"} ($reason)")
|
||||||
|
} catch (_: Throwable) {
|
||||||
|
Log.d(TAG_FG, "native toggle not available ($reason)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val enablePresentWhenReady = object : Runnable {
|
||||||
|
override fun run() {
|
||||||
|
val isReallyResumed = lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED) && isActive
|
||||||
|
val hasFocusNow = hasWindowFocus()
|
||||||
|
val rendererReady = mainViewModel?.rendererReady == true
|
||||||
|
|
||||||
|
if (!isReallyResumed || !hasFocusNow || !rendererReady) {
|
||||||
|
handler.postDelayed(this, ENABLE_PRESENT_DELAY_MS)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
setPresentEnabled(true, "focus regained + delay")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val reattachWindowWhenReady = object : Runnable {
|
||||||
|
override fun run() {
|
||||||
|
val isReallyResumed = lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED) && isActive
|
||||||
|
val hasFocusNow = hasWindowFocus()
|
||||||
|
if (!isReallyResumed || !hasFocusNow) {
|
||||||
|
handler.postDelayed(this, REATTACH_DELAY_MS)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try { mainViewModel?.gameHost?.rebindNativeWindow(force = true) } catch (_: Throwable) {}
|
||||||
|
|
||||||
|
if (!KenjinxNative.reattachWindowIfReady()) {
|
||||||
|
handler.postDelayed(this, REATTACH_DELAY_MS)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
Log.d(TAG_FG, "window reattached")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun doOrientationPulse(currentRot: Int) {
|
||||||
|
val now = android.os.SystemClock.uptimeMillis()
|
||||||
|
if (pulsingOrientation || now - lastPulseAt < 350L) return
|
||||||
|
pulsingOrientation = true
|
||||||
|
lastPulseAt = now
|
||||||
|
|
||||||
|
// Short lock on the target page (prevents flickering)
|
||||||
|
val lock = if (currentRot == Surface.ROTATION_90)
|
||||||
|
ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
|
||||||
|
else
|
||||||
|
ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE
|
||||||
|
|
||||||
|
try { requestedOrientation = lock } catch (_: Throwable) {}
|
||||||
|
handler.post {
|
||||||
|
if (isGameRunning) {
|
||||||
|
try { KenjinxNative.setSurfaceRotationByAndroidRotation(currentRot) } catch (_: Throwable) {}
|
||||||
|
try { mainViewModel?.gameHost?.onOrientationOrSizeChanged(currentRot) } catch (_: Throwable) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// After a short time back to SENSOR_LANDSCAPE
|
||||||
|
handler.postDelayed({
|
||||||
|
try { requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE } catch (_: Throwable) {}
|
||||||
|
handler.post {
|
||||||
|
if (isGameRunning) {
|
||||||
|
try {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
|
KenjinxNative.setSurfaceRotationByAndroidRotation(display?.rotation)
|
||||||
|
}
|
||||||
|
} catch (_: Throwable) {}
|
||||||
|
try {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
|
mainViewModel?.gameHost?.onOrientationOrSizeChanged(display?.rotation)
|
||||||
|
}
|
||||||
|
} catch (_: Throwable) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pulsingOrientation = false
|
||||||
|
}, 250)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
var mainViewModel: MainViewModel? = null
|
||||||
|
var AppPath: String = ""
|
||||||
|
var StorageHelper: SimpleStorageHelper? = null
|
||||||
|
|
||||||
|
const val EXTRA_BOOT_PATH = "bootPath"
|
||||||
|
const val EXTRA_FORCE_NCE_PPTC = "forceNceAndPptc"
|
||||||
|
const val EXTRA_TITLE_ID = "titleId"
|
||||||
|
const val EXTRA_TITLE_NAME = "titleName"
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.R)
|
||||||
|
@JvmStatic
|
||||||
|
fun frameEnded() {
|
||||||
|
mainViewModel?.activity?.apply {
|
||||||
|
if (isActive && QuickSettings(this).enablePerformanceMode) {
|
||||||
|
mainViewModel?.performanceManager?.setTurboMode(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mainViewModel?.gameHost?.hideProgressIndicator()
|
||||||
|
}
|
||||||
|
|
||||||
|
// <<< is called from the Native/Lib page to set the loading progress
|
||||||
|
@JvmStatic
|
||||||
|
fun updateProgress(info: String, percent: Float) {
|
||||||
|
// Route directly via the GameHost – it takes care of the progress states
|
||||||
|
mainViewModel?.gameHost?.setProgress(info, percent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
storageHelper = SimpleStorageHelper(this)
|
||||||
|
StorageHelper = storageHelper
|
||||||
|
System.loadLibrary("kenjinxjni")
|
||||||
|
initVm()
|
||||||
|
}
|
||||||
|
|
||||||
|
private external fun initVm()
|
||||||
|
|
||||||
|
private fun initialize() {
|
||||||
|
if (_isInit) return
|
||||||
|
val appPath: String = AppPath
|
||||||
|
|
||||||
|
val quickSettings = QuickSettings(this)
|
||||||
|
KenjinxNative.loggingSetEnabled(LogLevel.Info, quickSettings.enableInfoLogs)
|
||||||
|
KenjinxNative.loggingSetEnabled(LogLevel.Stub, quickSettings.enableStubLogs)
|
||||||
|
KenjinxNative.loggingSetEnabled(LogLevel.Warning, quickSettings.enableWarningLogs)
|
||||||
|
KenjinxNative.loggingSetEnabled(LogLevel.Error, quickSettings.enableErrorLogs)
|
||||||
|
KenjinxNative.loggingSetEnabled(LogLevel.AccessLog, quickSettings.enableFsAccessLogs)
|
||||||
|
KenjinxNative.loggingSetEnabled(LogLevel.Guest, quickSettings.enableGuestLogs)
|
||||||
|
KenjinxNative.loggingSetEnabled(LogLevel.Trace, quickSettings.enableTraceLogs)
|
||||||
|
KenjinxNative.loggingSetEnabled(LogLevel.Debug, quickSettings.enableDebugLogs)
|
||||||
|
KenjinxNative.loggingEnabledGraphicsLog(quickSettings.enableGraphicsLogs)
|
||||||
|
|
||||||
|
_isInit = KenjinxNative.javaInitialize(appPath, JNIEnv.CURRENT)
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.R)
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
ensureNotificationPermission()
|
||||||
|
|
||||||
|
motionSensorManager = MotionSensorManager(this)
|
||||||
|
Thread.setDefaultUncaughtExceptionHandler(crashHandler)
|
||||||
|
|
||||||
|
if (!Environment.isExternalStorageManager()
|
||||||
|
) {
|
||||||
|
storageHelper?.storage?.requestFullStorageAccess()
|
||||||
|
}
|
||||||
|
|
||||||
|
AppPath = this.getExternalFilesDir(null)!!.absolutePath
|
||||||
|
|
||||||
|
coldResetIfZombie("onCreate")
|
||||||
|
initialize()
|
||||||
|
|
||||||
|
window.attributes.layoutInDisplayCutoutMode =
|
||||||
|
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
|
||||||
|
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||||
|
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||||
|
|
||||||
|
// Apply alignment
|
||||||
|
applyOrientationPreference()
|
||||||
|
|
||||||
|
WindowInsetsControllerCompat(window, window.decorView).let { controller ->
|
||||||
|
controller.hide(WindowInsetsCompat.Type.systemBars())
|
||||||
|
controller.systemBarsBehavior =
|
||||||
|
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
|
||||||
|
}
|
||||||
|
|
||||||
|
uiHandler = UiHandler()
|
||||||
|
displayManager = getSystemService(DISPLAY_SERVICE) as DisplayManager
|
||||||
|
|
||||||
|
mainViewModel = MainViewModel(this)
|
||||||
|
mainViewModel!!.physicalControllerManager = physicalControllerManager
|
||||||
|
mainViewModel!!.motionSensorManager = motionSensorManager
|
||||||
|
mainViewModel!!.refreshFirmwareVersion()
|
||||||
|
|
||||||
|
mainViewModel?.apply {
|
||||||
|
setContent {
|
||||||
|
KenjinxAndroidTheme {
|
||||||
|
Surface(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
color = MaterialTheme.colorScheme.background
|
||||||
|
) {
|
||||||
|
MainView.Main(mainViewModel = this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
storedIntent = intent
|
||||||
|
rotLog("onCreate: initial display.rotation=${display?.rotation} → ${deg(display?.rotation)}°")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onNewIntent(intent: Intent) {
|
||||||
|
super.onNewIntent(intent)
|
||||||
|
storedIntent = intent
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSaveInstanceState(outState: Bundle) {
|
||||||
|
storageHelper?.onSaveInstanceState(outState)
|
||||||
|
super.onSaveInstanceState(outState)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
|
||||||
|
super.onRestoreInstanceState(savedInstanceState)
|
||||||
|
storageHelper?.onRestoreInstanceState(savedInstanceState)
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("RestrictedApi")
|
||||||
|
override fun dispatchKeyEvent(event: KeyEvent): Boolean {
|
||||||
|
event.apply { if (physicalControllerManager.onKeyEvent(this)) return true }
|
||||||
|
return super.dispatchKeyEvent(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun dispatchGenericMotionEvent(ev: MotionEvent?): Boolean {
|
||||||
|
ev?.apply { physicalControllerManager.onMotionEvent(this) }
|
||||||
|
return super.dispatchGenericMotionEvent(ev)
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Audio foreground/background gating ---
|
||||||
|
private fun setAudioForegroundState(inForeground: Boolean) {
|
||||||
|
// Preferred: pause instead of just muting
|
||||||
|
try { KenjinxNative.audioSetPaused(!inForeground) } catch (_: Throwable) {}
|
||||||
|
// Fallback: Master Mute
|
||||||
|
try { KenjinxNative.audioSetMuted(!inForeground) } catch (_: Throwable) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------- BACKGROUND STABILITY: Present gating ---------
|
||||||
|
override fun onStart() {
|
||||||
|
super.onStart()
|
||||||
|
coldResetIfZombie("onStart")
|
||||||
|
|
||||||
|
if (isGameRunning && mainViewModel?.rendererReady == true) {
|
||||||
|
try {
|
||||||
|
KenjinxNative.graphicsSetPresentEnabled(true)
|
||||||
|
Log.d(TAG_FG, "present=ENABLED (onStart)")
|
||||||
|
} catch (_: Throwable) {}
|
||||||
|
} else {
|
||||||
|
Log.d(TAG_FG, "skip enable present (onStart) — rendererReady=${mainViewModel?.rendererReady}")
|
||||||
|
setPresentEnabled(false, "cold reset: onStart (no game)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStop() {
|
||||||
|
super.onStop()
|
||||||
|
if (isGameRunning) {
|
||||||
|
setAudioForegroundState(false)
|
||||||
|
handler.removeCallbacks(reattachWindowWhenReady)
|
||||||
|
handler.removeCallbacks(enablePresentWhenReady)
|
||||||
|
setPresentEnabled(false, "onStop")
|
||||||
|
try { KenjinxNative.detachWindow() } catch (_: Throwable) {}
|
||||||
|
}
|
||||||
|
// IMPORTANT: Safely release the binding (prevents memory leak)
|
||||||
|
try { mainViewModel?.gameHost?.shutdownBinding() } catch (_: Throwable) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onTrimMemory(level: Int) {
|
||||||
|
super.onTrimMemory(level)
|
||||||
|
if (level >= TRIM_MEMORY_UI_HIDDEN && isGameRunning) {
|
||||||
|
setAudioForegroundState(false)
|
||||||
|
if (mainViewModel?.rendererReady == true) {
|
||||||
|
try {
|
||||||
|
setPresentEnabled(false, "onTrimMemory:$level")
|
||||||
|
KenjinxNative.detachWindow()
|
||||||
|
} catch (_: Throwable) {}
|
||||||
|
} else {
|
||||||
|
Log.d(TAG_FG, "skip disable present (onTrimMemory) — rendererReady=${mainViewModel?.rendererReady}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// --------------------------------------------------------
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
isActive = true
|
||||||
|
setAudioForegroundState(true)
|
||||||
|
|
||||||
|
coldResetIfZombie("onResume")
|
||||||
|
|
||||||
|
applyOrientationPreference()
|
||||||
|
|
||||||
|
// Enable display listener
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
|
lastKnownRotation = display?.rotation
|
||||||
|
rotLog("onResume: display.rotation=${display?.rotation} → ${deg(display?.rotation)}°")
|
||||||
|
}
|
||||||
|
|
||||||
|
try { displayManager.registerDisplayListener(displayListener, handler) } catch (_: Throwable) {}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (Build.VERSION.SDK_INT >= 33) {
|
||||||
|
registerReceiver(
|
||||||
|
serviceStopReceiver,
|
||||||
|
IntentFilter(EmulationService.ACTION_STOPPED),
|
||||||
|
RECEIVER_EXPORTED
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
registerReceiver(serviceStopReceiver, IntentFilter(EmulationService.ACTION_STOPPED))
|
||||||
|
}
|
||||||
|
} catch (_: Throwable) {}
|
||||||
|
|
||||||
|
handler.removeCallbacks(reattachWindowWhenReady)
|
||||||
|
handler.removeCallbacks(enablePresentWhenReady)
|
||||||
|
|
||||||
|
try { mainViewModel?.gameHost?.rebindNativeWindow(force = true) } catch (_: Throwable) {}
|
||||||
|
|
||||||
|
handler.postDelayed(delayedHandleIntent, 10)
|
||||||
|
|
||||||
|
if (isGameRunning && QuickSettings(this).enableMotion) {
|
||||||
|
motionSensorManager.register()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isGameRunning) {
|
||||||
|
handler.postDelayed(reattachWindowWhenReady, REATTACH_DELAY_MS)
|
||||||
|
if (hasWindowFocus()) {
|
||||||
|
handler.postDelayed(enablePresentWhenReady, ENABLE_PRESENT_DELAY_MS)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setPresentEnabled(false, "cold reset: onResume (no game)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onWindowFocusChanged(hasFocus: Boolean) {
|
||||||
|
super.onWindowFocusChanged(hasFocus)
|
||||||
|
if (!isGameRunning) return
|
||||||
|
|
||||||
|
handler.removeCallbacks(reattachWindowWhenReady)
|
||||||
|
handler.removeCallbacks(enablePresentWhenReady)
|
||||||
|
|
||||||
|
if (hasFocus && isActive) {
|
||||||
|
setAudioForegroundState(true)
|
||||||
|
// First ensure that the binding exists
|
||||||
|
try { mainViewModel?.gameHost?.ensureServiceStartedAndBound() } catch (_: Throwable) {}
|
||||||
|
|
||||||
|
setPresentEnabled(false, "focus gained → pre-rebind")
|
||||||
|
try { mainViewModel?.gameHost?.rebindNativeWindow(force = true) } catch (_: Throwable) {}
|
||||||
|
handler.postDelayed(reattachWindowWhenReady, 150L)
|
||||||
|
val rot = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) display?.rotation else null
|
||||||
|
handler.postDelayed({ try { mainViewModel?.gameHost?.postReattachKicks(rot) } catch (_: Throwable) {} }, 200L)
|
||||||
|
handler.postDelayed(enablePresentWhenReady, 450L)
|
||||||
|
} else {
|
||||||
|
setAudioForegroundState(false)
|
||||||
|
setPresentEnabled(false, "focus lost")
|
||||||
|
try { KenjinxNative.detachWindow() } catch (_: Throwable) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.R)
|
||||||
|
override fun onPause() {
|
||||||
|
super.onPause()
|
||||||
|
isActive = false
|
||||||
|
setAudioForegroundState(false)
|
||||||
|
|
||||||
|
handler.removeCallbacks(reattachWindowWhenReady)
|
||||||
|
handler.removeCallbacks(enablePresentWhenReady)
|
||||||
|
|
||||||
|
if (isGameRunning) {
|
||||||
|
setPresentEnabled(false, "onPause")
|
||||||
|
try { KenjinxNative.detachWindow() } catch (_: Throwable) {}
|
||||||
|
mainViewModel?.performanceManager?.setTurboMode(false)
|
||||||
|
motionSensorManager.unregister()
|
||||||
|
}
|
||||||
|
|
||||||
|
try { displayManager.unregisterDisplayListener(displayListener) } catch (_: Throwable) {}
|
||||||
|
try { unregisterReceiver(serviceStopReceiver) } catch (_: Throwable) {}
|
||||||
|
|
||||||
|
// Clean up binding (prevents memory leak when swiping away the task)
|
||||||
|
try { mainViewModel?.gameHost?.shutdownBinding() } catch (_: Throwable) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleIntent() {
|
||||||
|
when (storedIntent.action) {
|
||||||
|
Intent.ACTION_VIEW, "org.kenjinx.android.LAUNCH_GAME" -> {
|
||||||
|
val bootPathExtra = storedIntent.getStringExtra(EXTRA_BOOT_PATH)
|
||||||
|
val forceNceAndPptc = storedIntent.getBooleanExtra(EXTRA_FORCE_NCE_PPTC, false)
|
||||||
|
val titleId = storedIntent.getStringExtra(EXTRA_TITLE_ID) ?: ""
|
||||||
|
val titleName = storedIntent.getStringExtra(EXTRA_TITLE_NAME) ?: ""
|
||||||
|
val dataUri: Uri? = storedIntent.data
|
||||||
|
|
||||||
|
val chosenUri: Uri? = when {
|
||||||
|
!bootPathExtra.isNullOrEmpty() -> bootPathExtra.toUri()
|
||||||
|
dataUri != null -> dataUri
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chosenUri != null) {
|
||||||
|
val doc = when (chosenUri.scheme?.lowercase()) {
|
||||||
|
"content" -> DocumentFile.fromSingleUri(this, chosenUri)
|
||||||
|
"file" -> chosenUri.path?.let { File(it) }?.let { DocumentFile.fromFile(it) }
|
||||||
|
else -> {
|
||||||
|
chosenUri.path?.let { File(it) }?.takeIf { it.exists() }?.let { DocumentFile.fromFile(it) }
|
||||||
|
?: DocumentFile.fromSingleUri(this, chosenUri)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (doc != null && doc.exists()) {
|
||||||
|
val gameModel = GameModel(doc, this)
|
||||||
|
gameModel.getGameInfo()
|
||||||
|
mainViewModel?.loadGameModel?.value = gameModel
|
||||||
|
mainViewModel?.bootPath?.value = "gameItem_${gameModel.titleName}"
|
||||||
|
mainViewModel?.forceNceAndPptc?.value = forceNceAndPptc
|
||||||
|
storedIntent = Intent()
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
Log.w("ShortcutDebug", "DocumentFile not found or not accessible: $chosenUri")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (titleId.isNotEmpty() || titleName.isNotEmpty()) {
|
||||||
|
resolveGameByTitleIdOrName(titleId, titleName)?.let { doc ->
|
||||||
|
val gameModel = GameModel(doc, this)
|
||||||
|
gameModel.getGameInfo()
|
||||||
|
mainViewModel?.loadGameModel?.value = gameModel
|
||||||
|
mainViewModel?.bootPath?.value = "gameItem_${gameModel.titleName}"
|
||||||
|
mainViewModel?.forceNceAndPptc?.value = forceNceAndPptc
|
||||||
|
storedIntent = Intent()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun applyOrientationPreference() {
|
||||||
|
val pref = QuickSettings(this).orientationPreference
|
||||||
|
requestedOrientation = pref.value
|
||||||
|
val rot = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
|
this.display?.rotation
|
||||||
|
} else {
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
null
|
||||||
|
}
|
||||||
|
rotLog("applyOrientationPreference: rot=$rot → ${deg(rot)}°, pref=${pref.name}")
|
||||||
|
try { KenjinxNative.setSurfaceRotationByAndroidRotation(rot) } catch (_: Throwable) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.R)
|
||||||
|
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||||
|
super.onConfigurationChanged(newConfig)
|
||||||
|
val rot = this.display?.rotation
|
||||||
|
val old = lastKnownRotation
|
||||||
|
lastKnownRotation = rot
|
||||||
|
|
||||||
|
rotLog("onConfigurationChanged: display.rotation=$rot → ${deg(rot)}°")
|
||||||
|
|
||||||
|
try { KenjinxNative.setSurfaceRotationByAndroidRotation(rot) } catch (_: Throwable) {}
|
||||||
|
|
||||||
|
val pref = QuickSettings(this).orientationPreference
|
||||||
|
val shouldPropagate =
|
||||||
|
pref == QuickSettings.OrientationPreference.Sensor ||
|
||||||
|
pref == QuickSettings.OrientationPreference.SensorLandscape
|
||||||
|
|
||||||
|
if (shouldPropagate && isGameRunning) {
|
||||||
|
handler.post { try { mainViewModel?.gameHost?.onOrientationOrSizeChanged(rot) } catch (_: Throwable) {} }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pref == QuickSettings.OrientationPreference.SensorLandscape && old != null && rot != null) {
|
||||||
|
val isSideFlip = (old == Surface.ROTATION_90 && rot == Surface.ROTATION_270) ||
|
||||||
|
(old == Surface.ROTATION_270 && rot == Surface.ROTATION_90)
|
||||||
|
if (isSideFlip) doOrientationPulse(rot)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val requestNotifPerm = registerForActivityResult(
|
||||||
|
ActivityResultContracts.RequestPermission()
|
||||||
|
) { /* optional: Log/Toast */ }
|
||||||
|
|
||||||
|
private fun ensureNotificationPermission() {
|
||||||
|
if (Build.VERSION.SDK_INT >= 33) {
|
||||||
|
val granted = ContextCompat.checkSelfPermission(
|
||||||
|
this, Manifest.permission.POST_NOTIFICATIONS
|
||||||
|
) == PackageManager.PERMISSION_GRANTED
|
||||||
|
if (!granted) {
|
||||||
|
requestNotifPerm.launch(Manifest.permission.POST_NOTIFICATIONS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun resolveGameByTitleIdOrName(titleIdHex: String?, displayName: String?): DocumentFile? {
|
||||||
|
val gamesRoot = getDefaultGamesTree() ?: return null
|
||||||
|
for (child in gamesRoot.listFiles()) {
|
||||||
|
if (!child.isFile) continue
|
||||||
|
if (!displayName.isNullOrBlank()) {
|
||||||
|
val n = child.name ?: ""
|
||||||
|
if (n.contains(displayName, ignoreCase = true)) return child
|
||||||
|
}
|
||||||
|
if (!titleIdHex.isNullOrBlank()) {
|
||||||
|
val tid = getTitleIdFast(child)
|
||||||
|
if (tid != null && tid.equals(titleIdHex, ignoreCase = true)) return child
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!titleIdHex.isNullOrBlank()) {
|
||||||
|
for (child in gamesRoot.listFiles()) {
|
||||||
|
if (!child.isFile) continue
|
||||||
|
val tid = getTitleIdFast(child)
|
||||||
|
if (tid != null && tid.equals(titleIdHex, ignoreCase = true)) return child
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getDefaultGamesTree(): DocumentFile? {
|
||||||
|
val vm = mainViewModel
|
||||||
|
if (vm?.defaultGameFolderUri != null) {
|
||||||
|
return DocumentFile.fromTreeUri(this, vm.defaultGameFolderUri!!)
|
||||||
|
}
|
||||||
|
val prefs = PreferenceManager.getDefaultSharedPreferences(this)
|
||||||
|
val legacyPath = prefs.getString("gameFolder", null)
|
||||||
|
if (!legacyPath.isNullOrEmpty()) {
|
||||||
|
// Without a SAF URI, tree listing is not possible
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getTitleIdFast(file: DocumentFile): String? {
|
||||||
|
val name = file.name ?: return null
|
||||||
|
val dot = name.lastIndexOf('.')
|
||||||
|
if (dot <= 0 || dot >= name.length - 1) return null
|
||||||
|
val ext = name.substring(dot + 1).lowercase()
|
||||||
|
return try {
|
||||||
|
contentResolver.openFileDescriptor(file.uri, "r")?.use { pfd ->
|
||||||
|
val info = org.kenjinx.android.viewmodels.GameInfo()
|
||||||
|
KenjinxNative.deviceGetGameInfo(pfd.fd, ext, info)
|
||||||
|
info.TitleId?.lowercase()
|
||||||
|
}
|
||||||
|
} catch (_: Exception) { null }
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.R)
|
||||||
|
fun shutdownAndRestart() {
|
||||||
|
val packageManager = packageManager
|
||||||
|
val intent = packageManager.getLaunchIntentForPackage(packageName)
|
||||||
|
val componentName = intent?.component
|
||||||
|
val restartIntent = Intent.makeRestartActivityTask(componentName)
|
||||||
|
mainViewModel?.let { it.performanceManager?.setTurboMode(false) }
|
||||||
|
startActivity(restartIntent)
|
||||||
|
Runtime.getRuntime().exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
handler.removeCallbacks(enablePresentWhenReady)
|
||||||
|
handler.removeCallbacks(reattachWindowWhenReady)
|
||||||
|
// If the activity dies → the binding is guaranteed to be released
|
||||||
|
try { mainViewModel?.gameHost?.shutdownBinding() } catch (_: Throwable) {}
|
||||||
|
super.onDestroy()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------- Helpers ----------
|
||||||
|
|
||||||
|
private fun setEmuRunningFlag(value: Boolean) {
|
||||||
|
try {
|
||||||
|
getSharedPreferences(PREFS, MODE_PRIVATE)
|
||||||
|
.edit {
|
||||||
|
putBoolean(KEY_EMU_RUNNING, value)
|
||||||
|
}
|
||||||
|
} catch (_: Throwable) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun clearEmuRunningFlag() = setEmuRunningFlag(false)
|
||||||
|
|
||||||
|
private fun hardColdReset(reason: String) {
|
||||||
|
Log.d(TAG_FG, "Cold graphics reset ($reason)")
|
||||||
|
isGameRunning = false
|
||||||
|
mainViewModel?.rendererReady = false
|
||||||
|
|
||||||
|
try { setPresentEnabled(false, "cold reset: $reason") } catch (_: Throwable) {}
|
||||||
|
try { KenjinxNative.detachWindow() } catch (_: Throwable) {}
|
||||||
|
|
||||||
|
try { stopService(Intent(this, EmulationService::class.java)) } catch (_: Throwable) {}
|
||||||
|
|
||||||
|
try { mainViewModel?.loadGameModel?.value = null } catch (_: Throwable) {}
|
||||||
|
try { mainViewModel?.bootPath?.value = "" } catch (_: Throwable) {}
|
||||||
|
try { mainViewModel?.forceNceAndPptc?.value = false } catch (_: Throwable) {}
|
||||||
|
storedIntent = Intent()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun coldResetIfZombie(phase: String) {
|
||||||
|
try {
|
||||||
|
val zombie = getSharedPreferences(PREFS, MODE_PRIVATE)
|
||||||
|
.getBoolean(KEY_EMU_RUNNING, false)
|
||||||
|
if (zombie) {
|
||||||
|
clearEmuRunningFlag()
|
||||||
|
setPresentEnabled(false, "kill stray: $phase")
|
||||||
|
hardColdReset("kill stray: $phase")
|
||||||
|
}
|
||||||
|
} catch (_: Throwable) { }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
package org.kenjinx.android
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.hardware.Sensor
|
||||||
|
import android.hardware.SensorEvent
|
||||||
|
import android.hardware.SensorEventListener2
|
||||||
|
import android.hardware.SensorManager
|
||||||
|
import android.view.OrientationEventListener
|
||||||
|
|
||||||
|
class MotionSensorManager(val activity: MainActivity) : SensorEventListener2 {
|
||||||
|
private var isRegistered: Boolean = false
|
||||||
|
private var gyro: Sensor?
|
||||||
|
private var accelerometer: Sensor?
|
||||||
|
private var sensorManager: SensorManager =
|
||||||
|
activity.getSystemService(Activity.SENSOR_SERVICE) as SensorManager
|
||||||
|
private var controllerId: Int = -1
|
||||||
|
|
||||||
|
private val motionGyroOrientation: FloatArray = FloatArray(3)
|
||||||
|
private val motionAcelOrientation: FloatArray = FloatArray(3)
|
||||||
|
|
||||||
|
init {
|
||||||
|
accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
|
||||||
|
gyro = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE)
|
||||||
|
setOrientation90()
|
||||||
|
object : OrientationEventListener(activity) {
|
||||||
|
override fun onOrientationChanged(orientation: Int) {
|
||||||
|
when {
|
||||||
|
isWithinOrientationRange(orientation, 270) -> {
|
||||||
|
setOrientation270()
|
||||||
|
}
|
||||||
|
|
||||||
|
isWithinOrientationRange(orientation, 90) -> {
|
||||||
|
setOrientation90()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isWithinOrientationRange(
|
||||||
|
currentOrientation: Int, targetOrientation: Int, epsilon: Int = 90
|
||||||
|
): Boolean {
|
||||||
|
return currentOrientation > targetOrientation - epsilon
|
||||||
|
&& currentOrientation < targetOrientation + epsilon
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setOrientation270() {
|
||||||
|
motionGyroOrientation[0] = -1.0f
|
||||||
|
motionGyroOrientation[1] = 1.0f
|
||||||
|
motionGyroOrientation[2] = 1.0f
|
||||||
|
motionAcelOrientation[0] = 1.0f
|
||||||
|
motionAcelOrientation[1] = -1.0f
|
||||||
|
motionAcelOrientation[2] = -1.0f
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setOrientation90() {
|
||||||
|
motionGyroOrientation[0] = 1.0f
|
||||||
|
motionGyroOrientation[1] = -1.0f
|
||||||
|
motionGyroOrientation[2] = 1.0f
|
||||||
|
motionAcelOrientation[0] = -1.0f
|
||||||
|
motionAcelOrientation[1] = 1.0f
|
||||||
|
motionAcelOrientation[2] = -1.0f
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setControllerId(id: Int) {
|
||||||
|
controllerId = id
|
||||||
|
}
|
||||||
|
|
||||||
|
fun register() {
|
||||||
|
if (isRegistered)
|
||||||
|
return
|
||||||
|
gyro?.apply {
|
||||||
|
sensorManager.registerListener(
|
||||||
|
this@MotionSensorManager,
|
||||||
|
gyro,
|
||||||
|
SensorManager.SENSOR_DELAY_GAME
|
||||||
|
)
|
||||||
|
}
|
||||||
|
accelerometer?.apply {
|
||||||
|
sensorManager.registerListener(
|
||||||
|
this@MotionSensorManager,
|
||||||
|
accelerometer,
|
||||||
|
SensorManager.SENSOR_DELAY_GAME
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
isRegistered = true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun unregister() {
|
||||||
|
sensorManager.unregisterListener(this)
|
||||||
|
isRegistered = false
|
||||||
|
|
||||||
|
if (controllerId != -1) {
|
||||||
|
KenjinxNative.inputSetAccelerometerData(0.0F, 0.0F, 0.0F, controllerId)
|
||||||
|
KenjinxNative.inputSetGyroData(0.0F, 0.0F, 0.0F, controllerId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSensorChanged(event: SensorEvent?) {
|
||||||
|
if (controllerId != -1)
|
||||||
|
if (isRegistered)
|
||||||
|
event?.apply {
|
||||||
|
when (sensor.type) {
|
||||||
|
Sensor.TYPE_ACCELEROMETER -> {
|
||||||
|
val x = motionAcelOrientation[0] * event.values[1]
|
||||||
|
val y = motionAcelOrientation[1] * event.values[0]
|
||||||
|
val z = motionAcelOrientation[2] * event.values[2]
|
||||||
|
|
||||||
|
KenjinxNative.inputSetAccelerometerData(
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
z,
|
||||||
|
controllerId
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Sensor.TYPE_GYROSCOPE -> {
|
||||||
|
val x = motionGyroOrientation[0] * event.values[1]
|
||||||
|
val y = motionGyroOrientation[1] * event.values[0]
|
||||||
|
val z = motionGyroOrientation[2] * event.values[2]
|
||||||
|
KenjinxNative.inputSetGyroData(x, y, z, controllerId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
KenjinxNative.inputSetAccelerometerData(0.0F, 0.0F, 0.0F, controllerId)
|
||||||
|
KenjinxNative.inputSetGyroData(0.0F, 0.0F, 0.0F, controllerId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {
|
||||||
|
// no-op
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFlushCompleted(sensor: Sensor?) {
|
||||||
|
// no-op
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package org.kenjinx.android
|
||||||
|
|
||||||
|
class NativeGraphicsInterop {
|
||||||
|
var VkCreateSurface: Long = 0
|
||||||
|
var SurfaceHandle: Long = 0
|
||||||
|
var VkRequiredExtensions: Array<String>? = null
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package org.kenjinx.android
|
||||||
|
|
||||||
|
import android.view.Surface
|
||||||
|
|
||||||
|
class NativeHelpers {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val instance = NativeHelpers()
|
||||||
|
|
||||||
|
init {
|
||||||
|
System.loadLibrary("kenjinxjni")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
external fun releaseNativeWindow(window: Long)
|
||||||
|
external fun getCreateSurfacePtr(): Long
|
||||||
|
external fun getNativeWindow(surface: Surface): Long
|
||||||
|
|
||||||
|
external fun loadDriver(
|
||||||
|
nativeLibPath: String,
|
||||||
|
privateAppsPath: String,
|
||||||
|
driverName: String
|
||||||
|
): Long
|
||||||
|
|
||||||
|
external fun setTurboMode(enable: Boolean)
|
||||||
|
external fun getMaxSwapInterval(nativeWindow: Long): Int
|
||||||
|
external fun getMinSwapInterval(nativeWindow: Long): Int
|
||||||
|
external fun setSwapInterval(nativeWindow: Long, swapInterval: Int): Int
|
||||||
|
external fun getStringJava(ptr: Long): String
|
||||||
|
external fun setIsInitialOrientationFlipped(isFlipped: Boolean)
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package org.kenjinx.android
|
||||||
|
|
||||||
|
import android.view.SurfaceView
|
||||||
|
|
||||||
|
class NativeWindow(val surface: SurfaceView) {
|
||||||
|
var nativePointer: Long
|
||||||
|
private val nativeHelpers: NativeHelpers = NativeHelpers.instance
|
||||||
|
private var _swapInterval: Int = 0
|
||||||
|
|
||||||
|
val maxSwapInterval: Int
|
||||||
|
get() {
|
||||||
|
return if (nativePointer == -1L) 0 else nativeHelpers.getMaxSwapInterval(nativePointer)
|
||||||
|
}
|
||||||
|
|
||||||
|
val minSwapInterval: Int
|
||||||
|
get() {
|
||||||
|
return if (nativePointer == -1L) 0 else nativeHelpers.getMinSwapInterval(nativePointer)
|
||||||
|
}
|
||||||
|
|
||||||
|
var swapInterval: Int
|
||||||
|
get() {
|
||||||
|
return _swapInterval
|
||||||
|
}
|
||||||
|
set(value) {
|
||||||
|
if (nativePointer == -1L || nativeHelpers.setSwapInterval(nativePointer, value) == 0)
|
||||||
|
_swapInterval = value
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
nativePointer = nativeHelpers.getNativeWindow(surface.holder.surface)
|
||||||
|
|
||||||
|
swapInterval = maxOf(1, minSwapInterval)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun requeryWindowHandle(): Long {
|
||||||
|
nativePointer = nativeHelpers.getNativeWindow(surface.holder.surface)
|
||||||
|
|
||||||
|
swapInterval = swapInterval
|
||||||
|
|
||||||
|
return nativePointer
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package org.kenjinx.android
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Build
|
||||||
|
import androidx.annotation.RequiresApi
|
||||||
|
import kotlin.math.abs
|
||||||
|
|
||||||
|
class PerformanceManager(private val activity: MainActivity) {
|
||||||
|
companion object {
|
||||||
|
@RequiresApi(Build.VERSION_CODES.R)
|
||||||
|
fun force60HzRefreshRate(enable: Boolean, activity: MainActivity) {
|
||||||
|
// Hack for MIUI devices since they don't support the standard Android APIs
|
||||||
|
try {
|
||||||
|
val setFpsIntent = Intent("com.miui.powerkeeper.SET_ACTIVITY_FPS").apply {
|
||||||
|
putExtra("package_name", "org.kenjinx.android")
|
||||||
|
putExtra("isEnter", enable)
|
||||||
|
}
|
||||||
|
activity.sendBroadcast(setFpsIntent)
|
||||||
|
} catch (_: Exception) {
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enable)
|
||||||
|
activity.display?.supportedModes?.minByOrNull { abs(it.refreshRate - 60f) }
|
||||||
|
?.let { activity.window.attributes.preferredDisplayModeId = it.modeId }
|
||||||
|
else
|
||||||
|
activity.display?.supportedModes?.maxByOrNull { it.refreshRate }
|
||||||
|
?.let { activity.window.attributes.preferredDisplayModeId = it.modeId }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.R)
|
||||||
|
fun setTurboMode(enable: Boolean) {
|
||||||
|
NativeHelpers.instance.setTurboMode(enable)
|
||||||
|
force60HzRefreshRate(enable, activity)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package org.kenjinx.android
|
||||||
|
|
||||||
|
import android.app.ActivityManager
|
||||||
|
import android.content.Context.ACTIVITY_SERVICE
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.compose.runtime.MutableState
|
||||||
|
import java.io.RandomAccessFile
|
||||||
|
|
||||||
|
object PerformanceMonitor {
|
||||||
|
fun getFrequencies(frequencies: MutableList<Double>){
|
||||||
|
frequencies.clear()
|
||||||
|
for (i in 0 until Runtime.getRuntime().availableProcessors()) {
|
||||||
|
runCatching {
|
||||||
|
val raf = RandomAccessFile("/sys/devices/system/cpu/cpu$i/cpufreq/scaling_cur_freq", "r")
|
||||||
|
|
||||||
|
frequencies.add(raf.use { it.readLine().toDouble() / 1000.0 })
|
||||||
|
}.onFailure {
|
||||||
|
Log.e("Performance Monitor", "Failed to read frequency of CPU core $i", it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getMemoryUsage(
|
||||||
|
usedMem: MutableState<Int>,
|
||||||
|
totalMem: MutableState<Int>) {
|
||||||
|
MainActivity.mainViewModel?.activity?.apply {
|
||||||
|
val actManager = getSystemService(ACTIVITY_SERVICE) as ActivityManager
|
||||||
|
val memInfo = ActivityManager.MemoryInfo()
|
||||||
|
actManager.getMemoryInfo(memInfo)
|
||||||
|
val availMemory = memInfo.availMem.toDouble() / (1024 * 1024)
|
||||||
|
val totalMemory = memInfo.totalMem.toDouble() / (1024 * 1024)
|
||||||
|
|
||||||
|
usedMem.value = (totalMemory - availMemory).toInt()
|
||||||
|
totalMem.value = totalMemory.toInt()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+194
@@ -0,0 +1,194 @@
|
|||||||
|
package org.kenjinx.android
|
||||||
|
|
||||||
|
import android.view.InputDevice
|
||||||
|
import android.view.KeyEvent
|
||||||
|
import android.view.MotionEvent
|
||||||
|
import org.kenjinx.android.viewmodels.QuickSettings
|
||||||
|
import kotlin.math.abs
|
||||||
|
|
||||||
|
class PhysicalControllerManager(val activity: MainActivity) {
|
||||||
|
private var controllerId: Int = -1
|
||||||
|
|
||||||
|
// Trigger debounce (analog → digital)
|
||||||
|
private var leftTriggerPressed = false
|
||||||
|
private var rightTriggerPressed = false
|
||||||
|
private val pressThreshold = 0.65f
|
||||||
|
private val releaseThreshold = 0.45f
|
||||||
|
|
||||||
|
fun onKeyEvent(event: KeyEvent): Boolean {
|
||||||
|
// Make sure we are connected
|
||||||
|
if (controllerId == -1) {
|
||||||
|
controllerId = KenjinxNative.inputConnectGamepad(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
val id = getGamePadButtonInputId(event.keyCode)
|
||||||
|
if (id != GamePadButtonInputId.None) {
|
||||||
|
val isNotFallback = (event.flags and KeyEvent.FLAG_FALLBACK) == 0
|
||||||
|
if (isNotFallback) {
|
||||||
|
when (event.action) {
|
||||||
|
KeyEvent.ACTION_UP -> KenjinxNative.inputSetButtonReleased(id.ordinal, controllerId)
|
||||||
|
KeyEvent.ACTION_DOWN -> KenjinxNative.inputSetButtonPressed(id.ordinal, controllerId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onMotionEvent(ev: MotionEvent) {
|
||||||
|
if (ev.action != MotionEvent.ACTION_MOVE) return
|
||||||
|
|
||||||
|
if (controllerId == -1) {
|
||||||
|
controllerId = KenjinxNative.inputConnectGamepad(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
val device = ev.device
|
||||||
|
val source = InputDevice.SOURCE_JOYSTICK
|
||||||
|
|
||||||
|
fun hasAxis(axis: Int): Boolean =
|
||||||
|
device?.getMotionRange(axis, source) != null
|
||||||
|
|
||||||
|
fun axisValue(axis: Int): Float = ev.getAxisValue(axis)
|
||||||
|
|
||||||
|
// --- Sticks (right side prefers RX/RY, fallback Z/RZ) ---
|
||||||
|
val rightXaxis = if (hasAxis(MotionEvent.AXIS_RX)) MotionEvent.AXIS_RX else MotionEvent.AXIS_Z
|
||||||
|
val rightYaxis = if (hasAxis(MotionEvent.AXIS_RY)) MotionEvent.AXIS_RY else MotionEvent.AXIS_RZ
|
||||||
|
|
||||||
|
val leftStickX = if (hasAxis(MotionEvent.AXIS_X)) axisValue(MotionEvent.AXIS_X) else 0f
|
||||||
|
val leftStickY = if (hasAxis(MotionEvent.AXIS_Y)) axisValue(MotionEvent.AXIS_Y) else 0f
|
||||||
|
val rightStickX = if (hasAxis(rightXaxis)) axisValue(rightXaxis) else 0f
|
||||||
|
val rightStickY = if (hasAxis(rightYaxis)) axisValue(rightYaxis) else 0f
|
||||||
|
|
||||||
|
KenjinxNative.inputSetStickAxis(1, leftStickX, -leftStickY, controllerId)
|
||||||
|
KenjinxNative.inputSetStickAxis(2, rightStickX, -rightStickY, controllerId)
|
||||||
|
|
||||||
|
// --- Reading the triggers (with fallbacks) ---
|
||||||
|
// Preferred: LTRIGGER/RTRIGGER, then BRAKE/GAS.
|
||||||
|
// If the right stick uses RX/RY (standard on Xbox), Z/RZ are free -> use them as a further fallback.
|
||||||
|
// If the stick uses Z/RZ, these are NOT used for triggers (to avoid conflicts).
|
||||||
|
val rightStickUsesZ = (rightXaxis == MotionEvent.AXIS_Z)
|
||||||
|
val rightStickUsesRZ = (rightYaxis == MotionEvent.AXIS_RZ)
|
||||||
|
|
||||||
|
val rawLT = when {
|
||||||
|
hasAxis(MotionEvent.AXIS_LTRIGGER) -> axisValue(MotionEvent.AXIS_LTRIGGER)
|
||||||
|
hasAxis(MotionEvent.AXIS_BRAKE) -> axisValue(MotionEvent.AXIS_BRAKE)
|
||||||
|
!rightStickUsesZ && hasAxis(MotionEvent.AXIS_Z) -> axisValue(MotionEvent.AXIS_Z)
|
||||||
|
else -> 0f
|
||||||
|
}
|
||||||
|
val rawRT = when {
|
||||||
|
hasAxis(MotionEvent.AXIS_RTRIGGER) -> axisValue(MotionEvent.AXIS_RTRIGGER)
|
||||||
|
hasAxis(MotionEvent.AXIS_GAS) -> axisValue(MotionEvent.AXIS_GAS)
|
||||||
|
!rightStickUsesRZ && hasAxis(MotionEvent.AXIS_RZ) -> axisValue(MotionEvent.AXIS_RZ)
|
||||||
|
else -> 0f
|
||||||
|
}
|
||||||
|
|
||||||
|
// Some pads produce slight offsets – normalize them
|
||||||
|
val lt = if (abs(rawLT) < 0.02f) 0f else rawLT.coerceIn(0f, 1f)
|
||||||
|
val rt = if (abs(rawRT) < 0.02f) 0f else rawRT.coerceIn(0f, 1f)
|
||||||
|
|
||||||
|
// Analog to digital conversion with hysteresis
|
||||||
|
if (!leftTriggerPressed && lt >= pressThreshold) {
|
||||||
|
leftTriggerPressed = true
|
||||||
|
KenjinxNative.inputSetButtonPressed(GamePadButtonInputId.LeftTrigger.ordinal, controllerId)
|
||||||
|
} else if (leftTriggerPressed && lt <= releaseThreshold) {
|
||||||
|
leftTriggerPressed = false
|
||||||
|
KenjinxNative.inputSetButtonReleased(GamePadButtonInputId.LeftTrigger.ordinal, controllerId)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rightTriggerPressed && rt >= pressThreshold) {
|
||||||
|
rightTriggerPressed = true
|
||||||
|
KenjinxNative.inputSetButtonPressed(GamePadButtonInputId.RightTrigger.ordinal, controllerId)
|
||||||
|
} else if (rightTriggerPressed && rt <= releaseThreshold) {
|
||||||
|
rightTriggerPressed = false
|
||||||
|
KenjinxNative.inputSetButtonReleased(GamePadButtonInputId.RightTrigger.ordinal, controllerId)
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- DPAD as HAT (as before) ---
|
||||||
|
device?.apply {
|
||||||
|
if (sources and InputDevice.SOURCE_DPAD != InputDevice.SOURCE_DPAD) {
|
||||||
|
val dPadHor = ev.getAxisValue(MotionEvent.AXIS_HAT_X)
|
||||||
|
val dPadVert = ev.getAxisValue(MotionEvent.AXIS_HAT_Y)
|
||||||
|
|
||||||
|
if (dPadVert == 0.0f) {
|
||||||
|
KenjinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadUp.ordinal, controllerId)
|
||||||
|
KenjinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadDown.ordinal, controllerId)
|
||||||
|
}
|
||||||
|
if (dPadHor == 0.0f) {
|
||||||
|
KenjinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadLeft.ordinal, controllerId)
|
||||||
|
KenjinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadRight.ordinal, controllerId)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dPadVert < 0.0f) {
|
||||||
|
KenjinxNative.inputSetButtonPressed(GamePadButtonInputId.DpadUp.ordinal, controllerId)
|
||||||
|
KenjinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadDown.ordinal, controllerId)
|
||||||
|
}
|
||||||
|
if (dPadHor < 0.0f) {
|
||||||
|
KenjinxNative.inputSetButtonPressed(GamePadButtonInputId.DpadLeft.ordinal, controllerId)
|
||||||
|
KenjinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadRight.ordinal, controllerId)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dPadVert > 0.0f) {
|
||||||
|
KenjinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadUp.ordinal, controllerId)
|
||||||
|
KenjinxNative.inputSetButtonPressed(GamePadButtonInputId.DpadDown.ordinal, controllerId)
|
||||||
|
}
|
||||||
|
if (dPadHor > 0.0f) {
|
||||||
|
KenjinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadLeft.ordinal, controllerId)
|
||||||
|
KenjinxNative.inputSetButtonPressed(GamePadButtonInputId.DpadRight.ordinal, controllerId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun connect(): Int {
|
||||||
|
controllerId = KenjinxNative.inputConnectGamepad(0)
|
||||||
|
return controllerId
|
||||||
|
}
|
||||||
|
|
||||||
|
fun disconnect() {
|
||||||
|
controllerId = -1
|
||||||
|
// If a trigger got "stuck" during the disconnect, release it as a precaution
|
||||||
|
if (leftTriggerPressed) {
|
||||||
|
leftTriggerPressed = false
|
||||||
|
KenjinxNative.inputSetButtonReleased(GamePadButtonInputId.LeftTrigger.ordinal, controllerId)
|
||||||
|
}
|
||||||
|
if (rightTriggerPressed) {
|
||||||
|
rightTriggerPressed = false
|
||||||
|
KenjinxNative.inputSetButtonReleased(GamePadButtonInputId.RightTrigger.ordinal, controllerId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getGamePadButtonInputId(keycode: Int): GamePadButtonInputId {
|
||||||
|
val quickSettings = QuickSettings(activity)
|
||||||
|
return when (keycode) {
|
||||||
|
// ABXY (Switch/Xbox Layout)
|
||||||
|
KeyEvent.KEYCODE_BUTTON_A -> if (!quickSettings.useSwitchLayout) GamePadButtonInputId.A else GamePadButtonInputId.B
|
||||||
|
KeyEvent.KEYCODE_BUTTON_B -> if (!quickSettings.useSwitchLayout) GamePadButtonInputId.B else GamePadButtonInputId.A
|
||||||
|
KeyEvent.KEYCODE_BUTTON_X -> if (!quickSettings.useSwitchLayout) GamePadButtonInputId.X else GamePadButtonInputId.Y
|
||||||
|
KeyEvent.KEYCODE_BUTTON_Y -> if (!quickSettings.useSwitchLayout) GamePadButtonInputId.Y else GamePadButtonInputId.X
|
||||||
|
|
||||||
|
// Shoulder & Trigger (in case a pad sends them as keys)
|
||||||
|
KeyEvent.KEYCODE_BUTTON_L1 -> GamePadButtonInputId.LeftShoulder
|
||||||
|
KeyEvent.KEYCODE_BUTTON_L2 -> GamePadButtonInputId.LeftTrigger
|
||||||
|
KeyEvent.KEYCODE_BUTTON_R1 -> GamePadButtonInputId.RightShoulder
|
||||||
|
KeyEvent.KEYCODE_BUTTON_R2 -> GamePadButtonInputId.RightTrigger
|
||||||
|
|
||||||
|
// L3 / R3
|
||||||
|
KeyEvent.KEYCODE_BUTTON_THUMBL -> GamePadButtonInputId.LeftStickButton
|
||||||
|
KeyEvent.KEYCODE_BUTTON_THUMBR -> GamePadButtonInputId.RightStickButton
|
||||||
|
KeyEvent.KEYCODE_BUTTON_11 -> GamePadButtonInputId.LeftStickButton
|
||||||
|
KeyEvent.KEYCODE_BUTTON_12 -> GamePadButtonInputId.RightStickButton
|
||||||
|
|
||||||
|
// D-Pad
|
||||||
|
KeyEvent.KEYCODE_DPAD_UP -> GamePadButtonInputId.DpadUp
|
||||||
|
KeyEvent.KEYCODE_DPAD_DOWN -> GamePadButtonInputId.DpadDown
|
||||||
|
KeyEvent.KEYCODE_DPAD_LEFT -> GamePadButtonInputId.DpadLeft
|
||||||
|
KeyEvent.KEYCODE_DPAD_RIGHT -> GamePadButtonInputId.DpadRight
|
||||||
|
|
||||||
|
// Plus/Minus
|
||||||
|
KeyEvent.KEYCODE_BUTTON_START -> GamePadButtonInputId.Plus
|
||||||
|
KeyEvent.KEYCODE_BUTTON_SELECT -> GamePadButtonInputId.Minus
|
||||||
|
|
||||||
|
else -> GamePadButtonInputId.None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user