mirror of
https://git.ryujinx.app/projects/Kenji-NX.git
synced 2026-09-27 15:30:08 +02:00
Compare commits
335
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f904c82d76 | ||
|
|
48b11b71fa | ||
|
|
53bde335d9 | ||
|
|
ec0ec3a86a | ||
|
|
02d0b480e3 | ||
|
|
07de13a47b | ||
|
|
fc4c45ef1d | ||
|
|
52c6a1890e | ||
|
|
2288084b53 | ||
|
|
dd519a32b6 | ||
|
|
7be8829b0a | ||
|
|
6225e142fc | ||
|
|
9e3c03a606 | ||
|
|
8e9bcb2a8e | ||
|
|
7101f52c01 | ||
|
|
e80fc00462 | ||
|
|
33cbd29c23 | ||
|
|
cfc7c6039f | ||
|
|
ac6db0fe76 | ||
|
|
bed294bb92 | ||
|
|
ed70f7921e | ||
|
|
1a1fe53535 | ||
|
|
47e55acf21 | ||
|
|
0e5fead0f9 | ||
|
|
d1fb89f746 | ||
|
|
cc1861f9bb | ||
|
|
733bd0951b | ||
|
|
087e711e44 | ||
|
|
9c46452388 | ||
|
|
0657b00622 | ||
|
|
87cf0b0cbb | ||
|
|
954332aa2a | ||
|
|
e650d3fb04 | ||
|
|
d578c5e2a9 | ||
|
|
521a18f5a7 | ||
|
|
4b255a98db | ||
|
|
8b34270a22 | ||
|
|
9580f20caa | ||
|
|
152460929d | ||
|
|
7a5554a17f | ||
|
|
d5e4dfae56 | ||
|
|
28268ef7ef | ||
|
|
6c47892b94 | ||
|
|
b362f837c5 | ||
|
|
a82a721df6 | ||
|
|
297df259c3 | ||
|
|
3ace5deadf | ||
|
|
04aacb1385 | ||
|
|
ea91841e9b | ||
|
|
5734ad9090 | ||
|
|
1486d2db40 | ||
|
|
c54ca28843 | ||
|
|
a53e691185 | ||
|
|
bf8c902d13 | ||
|
|
f4dc400a47 | ||
|
|
a36882ab51 | ||
|
|
76a123b81d | ||
|
|
9d3fecf334 | ||
|
|
31d0bc5915 | ||
|
|
30dba1ed36 | ||
|
|
0d6c0aeeab | ||
|
|
74d6b310b8 | ||
|
|
3289715b8c | ||
|
|
3a3129252a | ||
|
|
ff47cb1d66 | ||
|
|
d440ff41d2 | ||
|
|
46308c32e7 | ||
|
|
bf09e153f1 | ||
|
|
0d1eebad31 | ||
|
|
b1b260ed17 | ||
|
|
a57756f447 | ||
|
|
be405de436 | ||
|
|
e88fd3273d | ||
|
|
79f55b12bf | ||
|
|
0bc515e4ff | ||
|
|
541fa63330 | ||
|
|
55aded3a05 | ||
|
|
61e0aba325 | ||
|
|
23fb4924e3 | ||
|
|
8fcc539ed9 | ||
|
|
51e4323a73 | ||
|
|
fcd55edf93 | ||
|
|
44872f45e0 | ||
|
|
1950b34e39 | ||
|
|
4aa8e249f1 | ||
|
|
3c13bdb4af | ||
|
|
7dec9a2f09 | ||
|
|
af6dd5b64c | ||
|
|
c6fba938bb | ||
|
|
4b4fd560ed | ||
|
|
7f99e32a0a | ||
|
|
4b8c74be10 | ||
|
|
2af1bc1808 | ||
|
|
1d19b318c1 | ||
|
|
36767d7fce | ||
|
|
20a9ecaebf | ||
|
|
77c947eb88 | ||
|
|
251b65771a | ||
|
|
7aa27b2ca5 | ||
|
|
3a479a437d | ||
|
|
5359ab77e1 | ||
|
|
3d5682375e | ||
|
|
bc777e1968 | ||
|
|
e729014cfa | ||
|
|
39b7242ef7 | ||
|
|
d55c0a702b | ||
|
|
f39b53982a | ||
|
|
46a57538c6 | ||
|
|
9b93b6fe18 | ||
|
|
d207d43768 | ||
|
|
d7f90f9f1f | ||
|
|
1b714ab1aa | ||
|
|
a6af70baa1 | ||
|
|
8c9cdfe9ce | ||
|
|
5b3a620bb0 | ||
|
|
d977208b19 | ||
|
|
477be704bb | ||
|
|
5c8be1b927 | ||
|
|
adc8ed594a | ||
|
|
9104a75a48 | ||
|
|
c0ba4e64cf | ||
|
|
6cf0a413a0 | ||
|
|
caed40d99a | ||
|
|
f764ae2d58 | ||
|
|
b16f74ffd9 | ||
|
|
bd96f2f51f | ||
|
|
3bf8a17f01 | ||
|
|
d5bd8871a2 | ||
|
|
dcafe32d5c | ||
|
|
18bd84c463 | ||
|
|
9a684ff066 | ||
|
|
60c867622e | ||
|
|
3cdae775b1 | ||
|
|
061bfb4074 | ||
|
|
2b15dfc473 | ||
|
|
a948308a2f | ||
|
|
a91f60b6ad | ||
|
|
56eef239a4 | ||
|
|
bef07155dc | ||
|
|
253f47c971 | ||
|
|
7d0947cac6 | ||
|
|
0d29b77a14 | ||
|
|
2aecce018c | ||
|
|
8ac2b1e497 | ||
|
|
9a1d33dde5 | ||
|
|
899c99740d | ||
|
|
1390fd02d7 | ||
|
|
5ecdd197cd | ||
|
|
546082128f | ||
|
|
2aae771ab3 | ||
|
|
a2c53ed6cf | ||
|
|
fba786cd38 | ||
|
|
292d42d394 | ||
|
|
afa4782493 | ||
|
|
b6cd6770ff | ||
|
|
fecb02647d | ||
|
|
bf12b601b0 | ||
|
|
4a5729cc01 | ||
|
|
5a16e24450 | ||
|
|
2dfa040d92 | ||
|
|
da6b58b9fa | ||
|
|
ad516777d9 | ||
|
|
812cecd713 | ||
|
|
d00bda7e53 | ||
|
|
c70f692324 | ||
|
|
9920619ff6 | ||
|
|
2ba33ec2ef | ||
|
|
e404954f88 | ||
|
|
6b383873e8 | ||
|
|
32d1653af9 | ||
|
|
298e2cdefd | ||
|
|
5a53d36992 | ||
|
|
8c99b90926 | ||
|
|
e287c9fc85 | ||
|
|
e349ef72fb | ||
|
|
b7c13715f1 | ||
|
|
139e18a8e1 | ||
|
|
58a76a0b9f | ||
|
|
6292192cc0 | ||
|
|
a926a31133 | ||
|
|
17b7eadb91 | ||
|
|
61a783e10a | ||
|
|
9b8517caf3 | ||
|
|
457f62d773 | ||
|
|
a6c0eabf49 | ||
|
|
e3ee28605d | ||
|
|
2c50fbcc1f | ||
|
|
90cb9d42db | ||
|
|
1a3b407839 | ||
|
|
46a0783293 | ||
|
|
82ae055fe5 | ||
|
|
f98af9e175 | ||
|
|
cdbe568620 | ||
|
|
86f47b8333 | ||
|
|
2993dba583 | ||
|
|
e720e4a87b | ||
|
|
f30d832847 | ||
|
|
afa1ab95c2 | ||
|
|
52b6fcf1a4 | ||
|
|
35449846d9 | ||
|
|
5d68312b54 | ||
|
|
b8aaadac5f | ||
|
|
a369b246a3 | ||
|
|
d96e93ef0d | ||
|
|
0bfd2ad3ef | ||
|
|
df36c23652 | ||
|
|
843a7113d1 | ||
|
|
2d420ee561 | ||
|
|
b958044e4e | ||
|
|
d659bec27a | ||
|
|
107df58666 | ||
|
|
280461dc78 | ||
|
|
1724149443 | ||
|
|
b44e2a87fd | ||
|
|
b1f767b2d5 | ||
|
|
ec9713b111 | ||
|
|
a7af26bc1f | ||
|
|
91a5f69805 | ||
|
|
f908bfe150 | ||
|
|
a2c494e216 | ||
|
|
e489e0d19f | ||
|
|
f362e33293 | ||
|
|
1e2fd6f4c2 | ||
|
|
35a8116ae8 | ||
|
|
41b8b6961c | ||
|
|
42727fca07 | ||
|
|
f5822c16af | ||
|
|
66a5719e10 | ||
|
|
cc2fb8f0b6 | ||
|
|
c9c78841b9 | ||
|
|
f26bd3d321 | ||
|
|
b28f52387a | ||
|
|
83412689b7 | ||
|
|
0b5f312b20 | ||
|
|
c0b7452cb5 | ||
|
|
6ba8ae53f2 | ||
|
|
d3bce3f361 | ||
|
|
2471bb8ede | ||
|
|
c3bfce3378 | ||
|
|
88e8d1309c | ||
|
|
ebeb532099 | ||
|
|
c501fe264b | ||
|
|
61d0430006 | ||
|
|
cd477cf744 | ||
|
|
0487b781dc | ||
|
|
51b32981b6 | ||
|
|
fa682d406e | ||
|
|
503dea74c2 | ||
|
|
1f979e5c6f | ||
|
|
378dc33cca | ||
|
|
9c37a557dd | ||
|
|
6e5bd0c9f2 | ||
|
|
0f2b11d4d5 | ||
|
|
1abf5b82c5 | ||
|
|
d751cfa81a | ||
|
|
af14fadb2f | ||
|
|
4fd4e3a4ff | ||
|
|
a1834900be | ||
|
|
fe7a30c747 | ||
|
|
3a387309b4 | ||
|
|
867a92a8bc | ||
|
|
45bf443c01 | ||
|
|
a09568d1b7 | ||
|
|
dae7ae7eaf | ||
|
|
c2f54d0a5c | ||
|
|
f4dfdd35b2 | ||
|
|
c56d04a9a9 | ||
|
|
5c6175cebf | ||
|
|
9addccb50f | ||
|
|
17946c78e6 | ||
|
|
29647b6351 | ||
|
|
6e6983799e | ||
|
|
61da23cb9e | ||
|
|
a62deb8cfd | ||
|
|
8ea79ae5d5 | ||
|
|
09f3bf4b55 | ||
|
|
171624e7f0 | ||
|
|
01cb33f658 | ||
|
|
121bb5dc76 | ||
|
|
d73ef0e3fb | ||
|
|
46b2da2435 | ||
|
|
aca897c70c | ||
|
|
e7d423cd07 | ||
|
|
48c809a80d | ||
|
|
7fd01214cf | ||
|
|
ef72e9b790 | ||
|
|
d5c9bc662c | ||
|
|
3cf90e808d | ||
|
|
326f744093 | ||
|
|
6cecbaa625 | ||
|
|
1443e1a2ff | ||
|
|
12d7f95468 | ||
|
|
b59a833467 | ||
|
|
50a2caf799 | ||
|
|
7d1fd99d3d | ||
|
|
c16559f20e | ||
|
|
6ecd6111d5 | ||
|
|
0ee71fa064 | ||
|
|
6e8f7e7fcb | ||
|
|
f9780b33bb | ||
|
|
d2a532f971 | ||
|
|
a90db3464d | ||
|
|
c61d171e52 | ||
|
|
8927a78c4d | ||
|
|
aa836dd924 | ||
|
|
878ea70aa9 | ||
|
|
9f0a35ee9b | ||
|
|
ed0ebdec5b | ||
|
|
b5a1272141 | ||
|
|
6768e1fa71 | ||
|
|
5c2d710fbb | ||
|
|
d3f3eacc41 | ||
|
|
5983cb9e5f | ||
|
|
e09f16838e | ||
|
|
18f302c0b0 | ||
|
|
8f77076117 | ||
|
|
39544e323c | ||
|
|
24932b7e1b | ||
|
|
2aaf34937d | ||
|
|
bce3426d26 | ||
|
|
51ca73cb10 | ||
|
|
cd3221abdc | ||
|
|
19dd23c288 | ||
|
|
20f1cef9b2 | ||
|
|
2f400491d5 | ||
|
|
f6351e3fff | ||
|
|
f15ddcf9c4 | ||
|
|
88d904acde | ||
|
|
fbcf57bef5 | ||
|
|
8bfb670e65 | ||
|
|
e00bece65a | ||
|
|
2a03f7b1d4 | ||
|
|
6c7b7d6fc4 | ||
|
|
c43db7d5f6 | ||
|
|
cc92af12a4 |
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"$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"
|
||||
}
|
||||
]
|
||||
}
|
||||
+4
-3
@@ -16,6 +16,10 @@ x64/
|
||||
build/
|
||||
[Bb]in/
|
||||
[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
|
||||
!packages/*/build/
|
||||
@@ -173,6 +177,3 @@ PublishProfiles/
|
||||
|
||||
# Glade backup files
|
||||
*.glade~
|
||||
|
||||
# Log files
|
||||
/logs/
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
[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,6 +1,7 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<DebugType Condition="'$(Configuration)'=='Release'">none</DebugType>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
||||
+21
-23
@@ -3,41 +3,41 @@
|
||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageVersion Include="Avalonia" Version="11.3.13" />
|
||||
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.3.13" />
|
||||
<PackageVersion Include="Avalonia.Desktop" Version="11.3.13" />
|
||||
<PackageVersion Include="Avalonia.Diagnostics" Version="11.3.13" />
|
||||
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="11.3.13" />
|
||||
<PackageVersion Include="Svg.Controls.Avalonia" Version="11.3.9.5" />
|
||||
<PackageVersion Include="Svg.Controls.Skia.Avalonia" Version="11.3.9.5" />
|
||||
<PackageVersion Include="Avalonia" Version="12.1.1" />
|
||||
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="12.1.2" />
|
||||
<PackageVersion Include="Avalonia.Desktop" Version="12.1.1" />
|
||||
<PackageVersion Include="AvaloniaUI.DiagnosticsSupport" Version="2.2.3" />
|
||||
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="12.1.1" />
|
||||
<PackageVersion Include="Svg.Controls.Avalonia" Version="12.0.0.15" />
|
||||
<PackageVersion Include="Svg.Controls.Skia.Avalonia" Version="12.0.0.15" />
|
||||
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
|
||||
<PackageVersion Include="CommunityToolkit.Mvvm" Version="8.4.2" />
|
||||
<PackageVersion Include="Concentus" Version="2.2.2" />
|
||||
<PackageVersion Include="DiscordRichPresence" Version="1.6.1.70" />
|
||||
<PackageVersion Include="DynamicData" Version="9.4.31" />
|
||||
<PackageVersion Include="FluentAvaloniaUI" Version="2.4.1" />
|
||||
<PackageVersion Include="Gommon" Version="2.8.1.1" />
|
||||
<PackageVersion Include="DynamicData" Version="9.4.33" />
|
||||
<PackageVersion Include="FluentAvaloniaUI" Version="3.1.0" />
|
||||
<PackageVersion Include="Gommon" Version="2.8.1.2" />
|
||||
<PackageVersion Include="Humanizer" Version="3.0.10" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.12.0" />
|
||||
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.17.0" />
|
||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.3.0" />
|
||||
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.21.0" />
|
||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.8.1" />
|
||||
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.1" />
|
||||
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
|
||||
<PackageVersion Include="NetCoreServer" Version="8.0.7" />
|
||||
<PackageVersion Include="Newtonsoft.Json" Version="13.0.4" />
|
||||
<PackageVersion Include="NUnit" Version="4.5.1" />
|
||||
<PackageVersion Include="NUnit" Version="4.6.1" />
|
||||
<PackageVersion Include="NUnit3TestAdapter" Version="6.2.0" />
|
||||
<PackageVersion Include="OpenTK.Core" Version="4.9.4" />
|
||||
<PackageVersion Include="OpenTK.Graphics" 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="Ryujinx.Audio.OpenAL" Version="1.25.2" />
|
||||
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies.Linux" Version="6.1.3-build5" />
|
||||
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies.Linux" Version="6.1.4-build6" />
|
||||
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies.macOS" Version="5.0.3-build14" />
|
||||
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies.Windows" Version="6.1.3-build5" />
|
||||
<PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" />
|
||||
<PackageVersion Include="Ryujinx.LibHac" Version="0.21.0-alpha.128" />
|
||||
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies.Windows" Version="6.1.4-build6" />
|
||||
<PackageVersion Include="Ryujinx.Graphics.Vulkan.MoltenVK" Version="1.4.2-ryujinx.6" />
|
||||
<PackageVersion Include="Ryujinx.LibHac" Version="0.21.0-alpha.133" />
|
||||
<PackageVersion Include="Ryujinx.SDL3-CS" Version="2026.707.0" />
|
||||
<PackageVersion Include="securifybv.ShellLink" Version="0.1.0" />
|
||||
<PackageVersion Include="SharpZipLib" Version="1.4.2" />
|
||||
@@ -45,12 +45,10 @@
|
||||
<PackageVersion Include="Silk.NET.Vulkan" Version="2.23.0" />
|
||||
<PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.23.0" />
|
||||
<PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.23.0" />
|
||||
<PackageVersion Include="SkiaSharp" Version="2.88.9" />
|
||||
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="2.88.9" />
|
||||
<PackageVersion Include="SkiaSharp" Version="3.119.4" />
|
||||
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="3.119.4" />
|
||||
<PackageVersion Include="SPB" Version="0.0.4-build32" />
|
||||
<PackageVersion Include="System.IO.Hashing" Version="9.0.12" />
|
||||
<PackageVersion Include="System.Management" Version="9.0.12" />
|
||||
<PackageVersion Include="System.IO.Hashing" Version="10.0.11" />
|
||||
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.1.0" />
|
||||
<PackageVersion Include="Rxmxnx.PInvoke.Extensions" Version="2.9.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
-261
@@ -1,261 +0,0 @@
|
||||
|
||||
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
|
||||
+7
-1
@@ -1,4 +1,10 @@
|
||||
<Solution>
|
||||
<Configurations>
|
||||
<BuildType Name="Debug" />
|
||||
<BuildType Name="Debug AOT" />
|
||||
<BuildType Name="Release" />
|
||||
<BuildType Name="Release AOT" />
|
||||
</Configurations>
|
||||
<Folder Name="/Solution Items/">
|
||||
<File Path=".editorconfig" />
|
||||
<File Path="Directory.Build.props" />
|
||||
@@ -6,7 +12,7 @@
|
||||
<File Path="nuget.config" />
|
||||
</Folder>
|
||||
<Project Path="src/ARMeilleure/ARMeilleure.csproj" />
|
||||
<Project Path="src/LibKenjinx/LibKenjinx.csproj" />
|
||||
<Project Path="src/Ryujinx.Audio.Backends.Apple/Ryujinx.Audio.Backends.Apple.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.SoundIo/Ryujinx.Audio.Backends.SoundIo.csproj" />
|
||||
|
||||
@@ -2,10 +2,6 @@
|
||||
|
||||
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
|
||||
RYUJINX_BIN="Ryujinx"
|
||||
fi
|
||||
|
||||
Executable
+3
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
CURRENTDIR="$(readlink -f "$(dirname "$0")")"
|
||||
exec "$CURRENTDIR"/usr/bin/Ryujinx.sh "$@"
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
#!/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,6 +10,8 @@
|
||||
<string>Ryujinx</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>Ryujinx.icns</string>
|
||||
<key>CFBundleIconName</key>
|
||||
<string>Ryujinx</string>
|
||||
<key>CFBundleDocumentTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
@@ -40,17 +42,17 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.1</string>
|
||||
<string>2.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.1.0</string>
|
||||
<string>2.0.0</string>
|
||||
<key>NSHighResolutionCapable</key>
|
||||
<true/>
|
||||
<key>CSResourcesFileMapped</key>
|
||||
<true/>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2018 - 2023 Ryujinx Team and Contributors.</string>
|
||||
<string>Copyright © 2018 - 2024 Ryujinx Team and Contributors. Copyright © 2024 - 2025 Kenji-NX and Contributors.</string>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.games</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
|
||||
Binary file not shown.
@@ -19,7 +19,7 @@ if platform.system() == "Darwin":
|
||||
else:
|
||||
OTOOL = shutil.which("llvm-otool")
|
||||
if OTOOL is None:
|
||||
for llvm_ver in [15, 14, 13]:
|
||||
for llvm_ver in [17, 16, 15, 14, 13]:
|
||||
otool_path = shutil.which(f"llvm-otool-{llvm_ver}")
|
||||
if otool_path is not None:
|
||||
OTOOL = otool_path
|
||||
@@ -562,26 +562,29 @@ search_path = [
|
||||
|
||||
|
||||
for path in content_directory.rglob("**/*.dylib"):
|
||||
current_search_path = [path.parent]
|
||||
current_search_path.extend(search_path)
|
||||
if not path.name.startswith("._"):
|
||||
current_search_path = [path.parent]
|
||||
current_search_path.extend(search_path)
|
||||
|
||||
fixup_dylib(
|
||||
path,
|
||||
get_path_related_to_target_exec(content_directory, path),
|
||||
current_search_path,
|
||||
content_directory,
|
||||
)
|
||||
print(f"Fixing path '{path}' using search path of '{current_search_path}' for context of '{content_directory}'.")
|
||||
fixup_dylib(
|
||||
path,
|
||||
get_path_related_to_target_exec(content_directory, path),
|
||||
current_search_path,
|
||||
content_directory,
|
||||
)
|
||||
|
||||
for path in content_directory.rglob("**/*.so"):
|
||||
current_search_path = [path.parent]
|
||||
current_search_path.extend(search_path)
|
||||
if not path.name.startswith("._"):
|
||||
current_search_path = [path.parent]
|
||||
current_search_path.extend(search_path)
|
||||
|
||||
fixup_dylib(
|
||||
path,
|
||||
get_path_related_to_target_exec(content_directory, path),
|
||||
current_search_path,
|
||||
content_directory,
|
||||
)
|
||||
fixup_dylib(
|
||||
path,
|
||||
get_path_related_to_target_exec(content_directory, path),
|
||||
current_search_path,
|
||||
content_directory,
|
||||
)
|
||||
|
||||
|
||||
with open(executable_path, "rb") as input:
|
||||
|
||||
@@ -26,7 +26,7 @@ else:
|
||||
LIPO = shutil.which("llvm-lipo")
|
||||
|
||||
if LIPO is None:
|
||||
for llvm_ver in [15, 14, 13]:
|
||||
for llvm_ver in [17, 16, 15, 14, 13]:
|
||||
lipo_path = shutil.which(f"llvm-lipo-{llvm_ver}")
|
||||
if lipo_path is not None:
|
||||
LIPO = lipo_path
|
||||
@@ -47,14 +47,12 @@ def get_new_name(
|
||||
input_component = str(input_dylib_path).replace(str(input_directory), "")[1:]
|
||||
return Path(os.path.join(output_directory, input_component))
|
||||
|
||||
|
||||
def is_fat_file(dylib_path: Path) -> str:
|
||||
res = subprocess.check_output([LIPO, "-info", str(dylib_path.absolute())]).decode(
|
||||
"utf-8"
|
||||
)
|
||||
|
||||
return not res.split("\n")[0].startswith("Non-fat file")
|
||||
|
||||
def get_archs(dylib_path: Path) -> list[str]:
|
||||
res = subprocess.check_output([LIPO, "-info", str(dylib_path)]).decode("utf-8")
|
||||
if res.startswith("Non-fat file"):
|
||||
return [res.split(":")[-1].strip()]
|
||||
else:
|
||||
return res.split("are:")[-1].strip().split()
|
||||
|
||||
def construct_universal_dylib(
|
||||
arm64_input_dylib_path: Path, x86_64_input_dylib_path: Path, output_dylib_path: Path
|
||||
@@ -69,11 +67,12 @@ def construct_universal_dylib(
|
||||
os.path.basename(arm64_input_dylib_path.resolve()), output_dylib_path
|
||||
)
|
||||
else:
|
||||
if is_fat_file(arm64_input_dylib_path) or not x86_64_input_dylib_path.exists():
|
||||
with open(output_dylib_path, "wb") as dst:
|
||||
with open(arm64_input_dylib_path, "rb") as src:
|
||||
dst.write(src.read())
|
||||
else:
|
||||
arm64_archs = get_archs(arm64_input_dylib_path)
|
||||
x86_64_archs = get_archs(x86_64_input_dylib_path) if x86_64_input_dylib_path.exists() else []
|
||||
|
||||
if "arm64" in arm64_archs and "x86_64" in arm64_archs:
|
||||
shutil.copy2(arm64_input_dylib_path, output_dylib_path)
|
||||
elif x86_64_archs:
|
||||
subprocess.check_call(
|
||||
[
|
||||
LIPO,
|
||||
|
||||
@@ -25,26 +25,41 @@ cp "$PUBLISH_DIRECTORY"/*.dylib "$APP_BUNDLE_DIRECTORY/Contents/Frameworks"
|
||||
cp Info.plist "$APP_BUNDLE_DIRECTORY/Contents"
|
||||
cp Ryujinx.icns "$APP_BUNDLE_DIRECTORY/Contents/Resources/Ryujinx.icns"
|
||||
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"
|
||||
|
||||
echo -n "APPL????" > "$APP_BUNDLE_DIRECTORY/Contents/PkgInfo"
|
||||
|
||||
# Fixup libraries and executable
|
||||
echo "Running bundle fix up python script"
|
||||
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
|
||||
echo "Starting signing process"
|
||||
if ! [ -x "$(command -v codesign)" ];
|
||||
then
|
||||
if ! [ -x "$(command -v rcodesign)" ];
|
||||
then
|
||||
echo "Cannot find rcodesign on your system, please install rcodesign."
|
||||
echo "Cannot find rcodesign on your system, please install rcodesign and ensure it is in your search path."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# cargo install apple-codesign
|
||||
echo "Usign rcodesign for ad-hoc signing"
|
||||
echo "Using 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"
|
||||
else
|
||||
echo "Usign codesign for ad-hoc signing"
|
||||
echo "Using 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"
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -20,7 +20,19 @@ SOURCE_REVISION_ID=$6
|
||||
CONFIGURATION=$7
|
||||
EXTRA_ARGS=$8
|
||||
|
||||
if [ "$VERSION" == "1.1.0" ];
|
||||
if [[ "$(uname)" == "Darwin" ]]; then
|
||||
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
|
||||
RELEASE_TAR_FILE_NAME=ryujinx-$CONFIGURATION-$VERSION+$SOURCE_REVISION_ID-macos_universal.app.tar
|
||||
else
|
||||
@@ -66,11 +78,11 @@ python3 "$BASE_DIR/distribution/macos/construct_universal_dylib.py" "$ARM64_APP_
|
||||
|
||||
if ! [ -x "$(command -v lipo)" ];
|
||||
then
|
||||
if ! [ -x "$(command -v llvm-lipo-14)" ];
|
||||
if ! [ -x "$(command -v llvm-lipo-17)" ];
|
||||
then
|
||||
LIPO=llvm-lipo
|
||||
else
|
||||
LIPO=llvm-lipo-14
|
||||
LIPO=llvm-lipo-17
|
||||
fi
|
||||
else
|
||||
LIPO=lipo
|
||||
@@ -109,12 +121,6 @@ python3 "$BASE_DIR/distribution/misc/add_tar_exec.py" "$RELEASE_TAR_FILE_NAME" "
|
||||
gzip -9 < "$RELEASE_TAR_FILE_NAME" > "$RELEASE_TAR_FILE_NAME.gz"
|
||||
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
|
||||
|
||||
echo "Done"
|
||||
echo "Done"
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
#!/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": {
|
||||
"version": "9.0.100",
|
||||
"version": "10.0.100",
|
||||
"rollForward": "latestFeature"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
<RuntimeIdentifiers>osx-arm64</RuntimeIdentifiers>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
<Configurations>Debug;Release;Debug AOT;Release AOT</Configurations>
|
||||
<Platforms>AnyCPU</Platforms>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace ARMeilleure.CodeGen
|
||||
/// <summary>
|
||||
/// Represents a compiled function.
|
||||
/// </summary>
|
||||
readonly struct CompiledFunction
|
||||
public readonly struct CompiledFunction
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the machine code of the <see cref="CompiledFunction"/>.
|
||||
@@ -44,10 +44,11 @@ namespace ARMeilleure.CodeGen
|
||||
/// <typeparamref name="T"/> pointing to the mapped function.
|
||||
/// </summary>
|
||||
/// <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>
|
||||
public T Map<T>()
|
||||
public T Map<T>(JitCache jitCache)
|
||||
{
|
||||
return MapWithPointer<T>(out _);
|
||||
return MapWithPointer<T>(jitCache, out _);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -55,11 +56,12 @@ namespace ARMeilleure.CodeGen
|
||||
/// <typeparamref name="T"/> pointing to the mapped function.
|
||||
/// </summary>
|
||||
/// <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>
|
||||
/// <returns>A delegate of type <typeparamref name="T"/> pointing to the mapped function</returns>
|
||||
public T MapWithPointer<T>(out nint codePointer)
|
||||
public T MapWithPointer<T>(JitCache jitCache, out nint codePointer)
|
||||
{
|
||||
codePointer = JitCache.Map(this);
|
||||
codePointer = jitCache.Map(this);
|
||||
|
||||
return Marshal.GetDelegateForFunctionPointer<T>(codePointer);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ namespace ARMeilleure.CodeGen.Linking
|
||||
/// <summary>
|
||||
/// Represents a relocation.
|
||||
/// </summary>
|
||||
readonly struct RelocEntry
|
||||
public readonly struct RelocEntry
|
||||
{
|
||||
public const int Stride = 13; // Bytes.
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace ARMeilleure.CodeGen.Linking
|
||||
/// <summary>
|
||||
/// Represents relocation information about a <see cref="CompiledFunction"/>.
|
||||
/// </summary>
|
||||
readonly struct RelocInfo
|
||||
public readonly struct RelocInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets an empty <see cref="RelocInfo"/>.
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace ARMeilleure.CodeGen.Linking
|
||||
/// <summary>
|
||||
/// Represents a symbol.
|
||||
/// </summary>
|
||||
readonly struct Symbol
|
||||
public readonly struct Symbol
|
||||
{
|
||||
private readonly ulong _value;
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ namespace ARMeilleure.CodeGen.Linking
|
||||
/// <summary>
|
||||
/// Types of <see cref="Symbol"/>.
|
||||
/// </summary>
|
||||
enum SymbolType : byte
|
||||
public enum SymbolType : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// Refers to nothing, i.e no symbol.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace ARMeilleure.CodeGen.Unwinding
|
||||
{
|
||||
struct UnwindInfo
|
||||
public struct UnwindInfo
|
||||
{
|
||||
public const int Stride = 4; // Bytes.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace ARMeilleure.CodeGen.Unwinding
|
||||
{
|
||||
enum UnwindPseudoOp
|
||||
public enum UnwindPseudoOp
|
||||
{
|
||||
PushReg = 0,
|
||||
SetFrame = 1,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace ARMeilleure.CodeGen.Unwinding
|
||||
{
|
||||
struct UnwindPushEntry
|
||||
public struct UnwindPushEntry
|
||||
{
|
||||
public const int Stride = 16; // Bytes.
|
||||
|
||||
|
||||
@@ -20,45 +20,21 @@ namespace ARMeilleure.Common
|
||||
new( 1, 6)
|
||||
];
|
||||
|
||||
private static readonly AddressTableLevel[] _levels64BitSparseTiny =
|
||||
private static readonly AddressTableLevel[] _levels64BitMono =
|
||||
[
|
||||
new( 11, 28),
|
||||
new( 2, 9)
|
||||
new( 2, 37)
|
||||
];
|
||||
|
||||
private static readonly AddressTableLevel[] _levels32BitSparseTiny =
|
||||
private static readonly AddressTableLevel[] _levels32BitMono =
|
||||
[
|
||||
new( 10, 22),
|
||||
new( 1, 9)
|
||||
new( 1, 31)
|
||||
];
|
||||
|
||||
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)
|
||||
|
||||
public static AddressTableLevel[] GetArmPreset(bool for64Bits, bool mono)
|
||||
{
|
||||
if (sparse)
|
||||
if (mono)
|
||||
{
|
||||
if (lowPower)
|
||||
{
|
||||
return for64Bits ? _levels64BitSparseTiny : _levels32BitSparseTiny;
|
||||
}
|
||||
else
|
||||
{
|
||||
return for64Bits ? _levels64BitSparseGiant : _levels32BitSparseGiant;
|
||||
}
|
||||
return for64Bits ? _levels64BitMono : _levels32BitMono;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace ARMeilleure.Common
|
||||
{
|
||||
public enum AddressTableType
|
||||
{
|
||||
Default,
|
||||
Sparse
|
||||
}
|
||||
}
|
||||
@@ -53,5 +53,14 @@ namespace ARMeilleure.Common
|
||||
{
|
||||
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,11 +5,10 @@ namespace ARMeilleure.Common
|
||||
public interface IAddressTable<TEntry> : IDisposable where TEntry : unmanaged
|
||||
{
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// Gets the <see cref="AddressTableType"/> of the <see cref="IAddressTable{TEntry}"/> instance.
|
||||
/// </summary>
|
||||
bool Sparse { get; }
|
||||
|
||||
AddressTableType TableType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the bits used by the <see cref="Levels"/> of the <see cref="IAddressTable{TEntry}"/> instance.
|
||||
/// </summary>
|
||||
@@ -31,6 +30,13 @@ namespace ARMeilleure.Common
|
||||
/// <exception cref="ObjectDisposedException"><see cref="EntryTable{TEntry}"/> instance was disposed</exception>
|
||||
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>
|
||||
/// Determines if the specified <paramref name="address"/> is in the range of the
|
||||
/// <see cref="IAddressTable{TEntry}"/>.
|
||||
|
||||
@@ -5,7 +5,7 @@ using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.State;
|
||||
using ARMeilleure.Translation;
|
||||
using ARMeilleure.Translation.PTC;
|
||||
|
||||
using System.Linq;
|
||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||
|
||||
@@ -147,6 +147,12 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
public static void EmitCall(ArmEmitterContext context, ulong immediate)
|
||||
{
|
||||
if (context.IsSingleStep)
|
||||
{
|
||||
context.Return(Const(immediate));
|
||||
return;
|
||||
}
|
||||
|
||||
bool isRecursive = immediate == context.EntryAddress;
|
||||
|
||||
if (isRecursive)
|
||||
@@ -161,12 +167,24 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
public static void EmitVirtualCall(ArmEmitterContext context, Operand target)
|
||||
{
|
||||
EmitTableBranch(context, target, isJump: false);
|
||||
if (context.IsSingleStep)
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (isReturn)
|
||||
if (isReturn || context.IsSingleStep)
|
||||
{
|
||||
EmitReturn(context, target);
|
||||
}
|
||||
@@ -218,9 +236,9 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
hostAddress = context.Load(OperandType.I64, hostAddressAddr);
|
||||
}
|
||||
else if (table.Sparse)
|
||||
else if (table.TableType == AddressTableType.Sparse)
|
||||
{
|
||||
// Inline table lookup. Only enabled when the sparse function table is enabled with 2 levels.
|
||||
// Inline table lookup. Only enabled when the sparse function table is enabled with 1 level.
|
||||
// Deliberately attempts to avoid branches.
|
||||
|
||||
Operand tableBase = !context.HasPtc ?
|
||||
@@ -229,24 +247,21 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
hostAddress = tableBase;
|
||||
|
||||
for (int i = 0; i < table.Levels.Length; i++)
|
||||
{
|
||||
AddressTableLevel level = table.Levels[i];
|
||||
int clearBits = 64 - (level.Index + level.Length);
|
||||
AddressTableLevel level = table.Levels.Last();
|
||||
int clearBits = 64 - (level.Index + level.Length);
|
||||
|
||||
Operand index = context.ShiftLeft(
|
||||
context.ShiftRightUI(context.ShiftLeft(guestAddress, Const(clearBits)), Const(clearBits + level.Index)),
|
||||
Const(3)
|
||||
);
|
||||
Operand index = context.ShiftLeft(
|
||||
context.ShiftRightUI(context.ShiftLeft(guestAddress, Const(clearBits)), Const(clearBits + level.Index)),
|
||||
Const(3)
|
||||
);
|
||||
|
||||
hostAddress = context.Load(OperandType.I64, context.Add(hostAddress, index));
|
||||
}
|
||||
hostAddress = context.Load(OperandType.I64, context.Add(hostAddress, index));
|
||||
}
|
||||
else
|
||||
{
|
||||
hostAddress = !context.HasPtc ?
|
||||
Const((long)context.Stubs.DispatchStub) :
|
||||
Const((long)context.Stubs.DispatchStub, Ptc.DispatchStubSymbol);
|
||||
Const(context.Stubs.DispatchStub) :
|
||||
Const(context.Stubs.DispatchStub, Ptc.DispatchStubSymbol);
|
||||
}
|
||||
|
||||
if (isJump)
|
||||
|
||||
@@ -406,7 +406,7 @@ namespace ARMeilleure.Instructions
|
||||
{
|
||||
Operand res = EmitSoftFloatCall(context, nameof(SoftFloat32.FPSub), op1, op2);
|
||||
|
||||
return EmitUnaryMathCall(context, nameof(MathHelper.Abs), res);
|
||||
return EmitUnaryMathCall(context, nameof(Math.Abs), res);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -451,7 +451,7 @@ namespace ARMeilleure.Instructions
|
||||
{
|
||||
Operand res = EmitSoftFloatCall(context, nameof(SoftFloat32.FPSub), op1, op2);
|
||||
|
||||
return EmitUnaryMathCall(context, nameof(MathHelper.Abs), res);
|
||||
return EmitUnaryMathCall(context, nameof(Math.Abs), res);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -483,7 +483,7 @@ namespace ARMeilleure.Instructions
|
||||
{
|
||||
EmitScalarUnaryOpF(context, (op1) =>
|
||||
{
|
||||
return EmitUnaryMathCall(context, nameof(MathHelper.Abs), op1);
|
||||
return EmitUnaryMathCall(context, nameof(Math.Abs), op1);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -522,7 +522,7 @@ namespace ARMeilleure.Instructions
|
||||
{
|
||||
EmitVectorUnaryOpF(context, (op1) =>
|
||||
{
|
||||
return EmitUnaryMathCall(context, nameof(MathHelper.Abs), op1);
|
||||
return EmitUnaryMathCall(context, nameof(Math.Abs), op1);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -2246,7 +2246,7 @@ namespace ARMeilleure.Instructions
|
||||
{
|
||||
EmitScalarUnaryOpF(context, (op1) =>
|
||||
{
|
||||
return EmitUnaryMathCall(context, nameof(MathHelper.Floor), op1);
|
||||
return EmitUnaryMathCall(context, nameof(Math.Floor), op1);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -2265,7 +2265,7 @@ namespace ARMeilleure.Instructions
|
||||
{
|
||||
EmitVectorUnaryOpF(context, (op1) =>
|
||||
{
|
||||
return EmitUnaryMathCall(context, nameof(MathHelper.Floor), op1);
|
||||
return EmitUnaryMathCall(context, nameof(Math.Floor), op1);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -2322,7 +2322,7 @@ namespace ARMeilleure.Instructions
|
||||
{
|
||||
EmitScalarUnaryOpF(context, (op1) =>
|
||||
{
|
||||
return EmitUnaryMathCall(context, nameof(MathHelper.Ceiling), op1);
|
||||
return EmitUnaryMathCall(context, nameof(Math.Ceiling), op1);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -2341,7 +2341,7 @@ namespace ARMeilleure.Instructions
|
||||
{
|
||||
EmitVectorUnaryOpF(context, (op1) =>
|
||||
{
|
||||
return EmitUnaryMathCall(context, nameof(MathHelper.Ceiling), op1);
|
||||
return EmitUnaryMathCall(context, nameof(Math.Ceiling), op1);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -2390,7 +2390,7 @@ namespace ARMeilleure.Instructions
|
||||
{
|
||||
EmitScalarUnaryOpF(context, (op1) =>
|
||||
{
|
||||
return EmitUnaryMathCall(context, nameof(MathHelper.Truncate), op1);
|
||||
return EmitUnaryMathCall(context, nameof(Math.Truncate), op1);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -2409,7 +2409,7 @@ namespace ARMeilleure.Instructions
|
||||
{
|
||||
EmitVectorUnaryOpF(context, (op1) =>
|
||||
{
|
||||
return EmitUnaryMathCall(context, nameof(MathHelper.Truncate), op1);
|
||||
return EmitUnaryMathCall(context, nameof(Math.Truncate), op1);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace ARMeilleure.Instructions
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitScalarUnaryOpF32(context, (op1) => EmitUnaryMathCall(context, nameof(MathHelper.Abs), op1));
|
||||
EmitScalarUnaryOpF32(context, (op1) => EmitUnaryMathCall(context, nameof(Math.Abs), op1));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ namespace ARMeilleure.Instructions
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitVectorUnaryOpF32(context, (op1) => EmitUnaryMathCall(context, nameof(MathHelper.Abs), op1));
|
||||
EmitVectorUnaryOpF32(context, (op1) => EmitUnaryMathCall(context, nameof(Math.Abs), op1));
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -726,8 +726,8 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
if (absolute)
|
||||
{
|
||||
ne = EmitUnaryMathCall(context, nameof(MathHelper.Abs), ne);
|
||||
me = EmitUnaryMathCall(context, nameof(MathHelper.Abs), me);
|
||||
ne = EmitUnaryMathCall(context, nameof(Math.Abs), ne);
|
||||
me = EmitUnaryMathCall(context, nameof(Math.Abs), me);
|
||||
}
|
||||
|
||||
Operand e = EmitSoftFloatCall(context, name, ne, me);
|
||||
|
||||
@@ -333,7 +333,7 @@ namespace ARMeilleure.Instructions
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitFcvt_s_Gp(context, (op1) => EmitUnaryMathCall(context, nameof(MathHelper.Floor), op1));
|
||||
EmitFcvt_s_Gp(context, (op1) => EmitUnaryMathCall(context, nameof(Math.Floor), op1));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -349,7 +349,7 @@ namespace ARMeilleure.Instructions
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitFcvt(context, (op1) => EmitUnaryMathCall(context, nameof(MathHelper.Floor), op1), signed: true, scalar: false);
|
||||
EmitFcvt(context, (op1) => EmitUnaryMathCall(context, nameof(Math.Floor), op1), signed: true, scalar: false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -365,7 +365,7 @@ namespace ARMeilleure.Instructions
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitFcvt_u_Gp(context, (op1) => EmitUnaryMathCall(context, nameof(MathHelper.Floor), op1));
|
||||
EmitFcvt_u_Gp(context, (op1) => EmitUnaryMathCall(context, nameof(Math.Floor), op1));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -538,7 +538,7 @@ namespace ARMeilleure.Instructions
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitFcvt_s_Gp(context, (op1) => EmitUnaryMathCall(context, nameof(MathHelper.Ceiling), op1));
|
||||
EmitFcvt_s_Gp(context, (op1) => EmitUnaryMathCall(context, nameof(Math.Ceiling), op1));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -554,7 +554,7 @@ namespace ARMeilleure.Instructions
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitFcvt_u_Gp(context, (op1) => EmitUnaryMathCall(context, nameof(MathHelper.Ceiling), op1));
|
||||
EmitFcvt_u_Gp(context, (op1) => EmitUnaryMathCall(context, nameof(Math.Ceiling), op1));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -357,10 +357,10 @@ namespace ARMeilleure.Instructions
|
||||
toConvert = EmitRoundMathCall(context, MidpointRounding.ToEven, toConvert);
|
||||
break;
|
||||
case 0b10: // Towards positive infinity
|
||||
toConvert = EmitUnaryMathCall(context, nameof(MathHelper.Ceiling), toConvert);
|
||||
toConvert = EmitUnaryMathCall(context, nameof(Math.Ceiling), toConvert);
|
||||
break;
|
||||
case 0b11: // Towards negative infinity
|
||||
toConvert = EmitUnaryMathCall(context, nameof(MathHelper.Floor), toConvert);
|
||||
toConvert = EmitUnaryMathCall(context, nameof(Math.Floor), toConvert);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -494,10 +494,10 @@ namespace ARMeilleure.Instructions
|
||||
toConvert = EmitRoundMathCall(context, MidpointRounding.ToEven, toConvert);
|
||||
break;
|
||||
case 0b10: // Towards positive infinity
|
||||
toConvert = EmitUnaryMathCall(context, nameof(MathHelper.Ceiling), toConvert);
|
||||
toConvert = EmitUnaryMathCall(context, nameof(Math.Ceiling), toConvert);
|
||||
break;
|
||||
case 0b11: // Towards negative infinity
|
||||
toConvert = EmitUnaryMathCall(context, nameof(MathHelper.Floor), toConvert);
|
||||
toConvert = EmitUnaryMathCall(context, nameof(Math.Floor), toConvert);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -534,7 +534,7 @@ namespace ARMeilleure.Instructions
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitVectorUnaryOpF32(context, (m) => EmitUnaryMathCall(context, nameof(MathHelper.Floor), m));
|
||||
EmitVectorUnaryOpF32(context, (m) => EmitUnaryMathCall(context, nameof(Math.Floor), m));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -574,7 +574,7 @@ namespace ARMeilleure.Instructions
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitVectorUnaryOpF32(context, (m) => EmitUnaryMathCall(context, nameof(MathHelper.Ceiling), m));
|
||||
EmitVectorUnaryOpF32(context, (m) => EmitUnaryMathCall(context, nameof(Math.Ceiling), m));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -613,7 +613,7 @@ namespace ARMeilleure.Instructions
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitScalarUnaryOpF32(context, (op1) => EmitUnaryMathCall(context, nameof(MathHelper.Truncate), op1));
|
||||
EmitScalarUnaryOpF32(context, (op1) => EmitUnaryMathCall(context, nameof(Math.Truncate), op1));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -460,8 +460,8 @@ namespace ARMeilleure.Instructions
|
||||
IOpCodeSimd op = (IOpCodeSimd)context.CurrOp;
|
||||
|
||||
MethodInfo info = (op.Size & 1) == 0
|
||||
? typeof(MathHelperF).GetMethod(name, [typeof(float)])
|
||||
: typeof(MathHelper).GetMethod(name, [typeof(double)]);
|
||||
? typeof(MathF).GetMethod(name, [typeof(float)])
|
||||
: typeof(Math).GetMethod(name, [typeof(double)]);
|
||||
|
||||
return context.Call(info, n);
|
||||
}
|
||||
@@ -470,11 +470,11 @@ namespace ARMeilleure.Instructions
|
||||
{
|
||||
IOpCodeSimd op = (IOpCodeSimd)context.CurrOp;
|
||||
|
||||
string name = nameof(MathHelper.Round);
|
||||
string name = nameof(Math.Round);
|
||||
|
||||
MethodInfo info = (op.Size & 1) == 0
|
||||
? typeof(MathHelperF).GetMethod(name, [typeof(float), typeof(int)])
|
||||
: typeof(MathHelper).GetMethod(name, [typeof(double), typeof(int)]);
|
||||
? typeof(MathF).GetMethod(name, [typeof(float), typeof(MidpointRounding)])
|
||||
: typeof(Math).GetMethod(name, [typeof(double), typeof(MidpointRounding)]);
|
||||
|
||||
return context.Call(info, n, Const((int)roundMode));
|
||||
}
|
||||
@@ -510,16 +510,16 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
context.MarkLabel(lbl1);
|
||||
context.BranchIf(lbl2, rMode, rP, Comparison.NotEqual);
|
||||
context.Copy(res, EmitUnaryMathCall(context, nameof(MathHelper.Ceiling), op));
|
||||
context.Copy(res, EmitUnaryMathCall(context, nameof(Math.Ceiling), op));
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lbl2);
|
||||
context.BranchIf(lbl3, rMode, rM, Comparison.NotEqual);
|
||||
context.Copy(res, EmitUnaryMathCall(context, nameof(MathHelper.Floor), op));
|
||||
context.Copy(res, EmitUnaryMathCall(context, nameof(Math.Floor), op));
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lbl3);
|
||||
context.Copy(res, EmitUnaryMathCall(context, nameof(MathHelper.Truncate), op));
|
||||
context.Copy(res, EmitUnaryMathCall(context, nameof(Math.Truncate), op));
|
||||
context.Branch(lblEnd);
|
||||
|
||||
context.MarkLabel(lblEnd);
|
||||
|
||||
@@ -1,115 +0,0 @@
|
||||
using System;
|
||||
#if ANDROID
|
||||
using System.Runtime.CompilerServices;
|
||||
#else
|
||||
using System.Runtime.InteropServices;
|
||||
#endif
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static class MathHelper
|
||||
{
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static double Abs(double value)
|
||||
{
|
||||
return Math.Abs(value);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static double Ceiling(double value)
|
||||
{
|
||||
return Math.Ceiling(value);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static double Floor(double value)
|
||||
{
|
||||
return Math.Floor(value);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static double Round(double value, int mode)
|
||||
{
|
||||
return Math.Round(value, (MidpointRounding)mode);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static double Truncate(double value)
|
||||
{
|
||||
return Math.Truncate(value);
|
||||
}
|
||||
}
|
||||
|
||||
static class MathHelperF
|
||||
{
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static float Abs(float value)
|
||||
{
|
||||
return MathF.Abs(value);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static float Ceiling(float value)
|
||||
{
|
||||
return MathF.Ceiling(value);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static float Floor(float value)
|
||||
{
|
||||
return MathF.Floor(value);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static float Round(float value, int mode)
|
||||
{
|
||||
return MathF.Round(value, (MidpointRounding)mode);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static float Truncate(float value)
|
||||
{
|
||||
return MathF.Truncate(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,11 +2,6 @@ using ARMeilleure.Memory;
|
||||
using ARMeilleure.State;
|
||||
using ARMeilleure.Translation;
|
||||
using System;
|
||||
#if ANDROID
|
||||
using System.Runtime.CompilerServices;
|
||||
#else
|
||||
using System.Runtime.InteropServices;
|
||||
#endif
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
@@ -39,11 +34,6 @@ namespace ARMeilleure.Instructions
|
||||
Context = null;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static void Break(ulong address, int imm)
|
||||
{
|
||||
Statistics.PauseTimer();
|
||||
@@ -53,11 +43,6 @@ namespace ARMeilleure.Instructions
|
||||
Statistics.ResumeTimer();
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static void SupervisorCall(ulong address, int imm)
|
||||
{
|
||||
Statistics.PauseTimer();
|
||||
@@ -67,11 +52,6 @@ namespace ARMeilleure.Instructions
|
||||
Statistics.ResumeTimer();
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static void Undefined(ulong address, int opCode)
|
||||
{
|
||||
Statistics.PauseTimer();
|
||||
@@ -82,51 +62,26 @@ namespace ARMeilleure.Instructions
|
||||
}
|
||||
|
||||
#region "System registers"
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static ulong GetCtrEl0()
|
||||
{
|
||||
return GetContext().CtrEl0;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static ulong GetDczidEl0()
|
||||
{
|
||||
return GetContext().DczidEl0;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static ulong GetCntfrqEl0()
|
||||
{
|
||||
return GetContext().CntfrqEl0;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static ulong GetCntpctEl0()
|
||||
{
|
||||
return GetContext().CntpctEl0;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static ulong GetCntvctEl0()
|
||||
{
|
||||
return GetContext().CntvctEl0;
|
||||
@@ -134,51 +89,26 @@ namespace ARMeilleure.Instructions
|
||||
#endregion
|
||||
|
||||
#region "Read"
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static byte ReadByte(ulong address)
|
||||
{
|
||||
return GetMemoryManager().ReadGuest<byte>(address);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static ushort ReadUInt16(ulong address)
|
||||
{
|
||||
return GetMemoryManager().ReadGuest<ushort>(address);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static uint ReadUInt32(ulong address)
|
||||
{
|
||||
return GetMemoryManager().ReadGuest<uint>(address);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static ulong ReadUInt64(ulong address)
|
||||
{
|
||||
return GetMemoryManager().ReadGuest<ulong>(address);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static V128 ReadVector128(ulong address)
|
||||
{
|
||||
return GetMemoryManager().ReadGuest<V128>(address);
|
||||
@@ -186,92 +116,47 @@ namespace ARMeilleure.Instructions
|
||||
#endregion
|
||||
|
||||
#region "Write"
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static void WriteByte(ulong address, byte value)
|
||||
{
|
||||
GetMemoryManager().WriteGuest(address, value);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static void WriteUInt16(ulong address, ushort value)
|
||||
{
|
||||
GetMemoryManager().WriteGuest(address, value);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static void WriteUInt32(ulong address, uint value)
|
||||
{
|
||||
GetMemoryManager().WriteGuest(address, value);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static void WriteUInt64(ulong address, ulong value)
|
||||
{
|
||||
GetMemoryManager().WriteGuest(address, value);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static void WriteVector128(ulong address, V128 value)
|
||||
{
|
||||
GetMemoryManager().WriteGuest(address, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static void EnqueueForRejit(ulong address)
|
||||
{
|
||||
Context.Translator.EnqueueForRejit(address, GetContext().ExecutionMode);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static void SignalMemoryTracking(ulong address, ulong size, byte write)
|
||||
public static void SignalMemoryTracking(ulong address, ulong size, bool write)
|
||||
{
|
||||
GetMemoryManager().SignalMemoryTracking(address, size, write == 1);
|
||||
GetMemoryManager().SignalMemoryTracking(address, size, write);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static void ThrowInvalidMemoryAccess(ulong address)
|
||||
{
|
||||
throw new InvalidAccessException(address);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static ulong GetFunctionAddress(ulong address)
|
||||
{
|
||||
TranslatedFunction function = Context.Translator.GetOrTranslate(address, GetContext().ExecutionMode);
|
||||
@@ -279,22 +164,12 @@ namespace ARMeilleure.Instructions
|
||||
return (ulong)function.FuncPointer.ToInt64();
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static void InvalidateCacheLine(ulong address)
|
||||
{
|
||||
Context.Translator.InvalidateJitCacheRegion(address, InstEmit.DczSizeInBytes);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static byte CheckSynchronization()
|
||||
public static bool CheckSynchronization()
|
||||
{
|
||||
Statistics.PauseTimer();
|
||||
|
||||
@@ -304,7 +179,7 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
Statistics.ResumeTimer();
|
||||
|
||||
return (byte)(context.Running ? 1 : 0);
|
||||
return context.Running;
|
||||
}
|
||||
|
||||
public static ExecutionContext GetContext()
|
||||
|
||||
@@ -1,21 +1,11 @@
|
||||
using ARMeilleure.State;
|
||||
using System;
|
||||
#if ANDROID
|
||||
using System.Runtime.CompilerServices;
|
||||
#else
|
||||
using System.Runtime.InteropServices;
|
||||
#endif
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static class SoftFallback
|
||||
{
|
||||
#region "ShrImm64"
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static long SignedShrImm64(long value, long roundConst, int shift)
|
||||
{
|
||||
if (roundConst == 0L)
|
||||
@@ -58,11 +48,6 @@ namespace ARMeilleure.Instructions
|
||||
}
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static ulong UnsignedShrImm64(ulong value, long roundConst, int shift)
|
||||
{
|
||||
if (roundConst == 0L)
|
||||
@@ -107,11 +92,6 @@ namespace ARMeilleure.Instructions
|
||||
#endregion
|
||||
|
||||
#region "Saturation"
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static int SatF32ToS32(float value)
|
||||
{
|
||||
if (float.IsNaN(value))
|
||||
@@ -123,11 +103,6 @@ namespace ARMeilleure.Instructions
|
||||
value <= int.MinValue ? int.MinValue : (int)value;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static long SatF32ToS64(float value)
|
||||
{
|
||||
if (float.IsNaN(value))
|
||||
@@ -139,11 +114,6 @@ namespace ARMeilleure.Instructions
|
||||
value <= long.MinValue ? long.MinValue : (long)value;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static uint SatF32ToU32(float value)
|
||||
{
|
||||
if (float.IsNaN(value))
|
||||
@@ -155,11 +125,6 @@ namespace ARMeilleure.Instructions
|
||||
value <= uint.MinValue ? uint.MinValue : (uint)value;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static ulong SatF32ToU64(float value)
|
||||
{
|
||||
if (float.IsNaN(value))
|
||||
@@ -171,11 +136,6 @@ namespace ARMeilleure.Instructions
|
||||
value <= ulong.MinValue ? ulong.MinValue : (ulong)value;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static int SatF64ToS32(double value)
|
||||
{
|
||||
if (double.IsNaN(value))
|
||||
@@ -187,11 +147,6 @@ namespace ARMeilleure.Instructions
|
||||
value <= int.MinValue ? int.MinValue : (int)value;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static long SatF64ToS64(double value)
|
||||
{
|
||||
if (double.IsNaN(value))
|
||||
@@ -203,11 +158,6 @@ namespace ARMeilleure.Instructions
|
||||
value <= long.MinValue ? long.MinValue : (long)value;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static uint SatF64ToU32(double value)
|
||||
{
|
||||
if (double.IsNaN(value))
|
||||
@@ -219,11 +169,6 @@ namespace ARMeilleure.Instructions
|
||||
value <= uint.MinValue ? uint.MinValue : (uint)value;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static ulong SatF64ToU64(double value)
|
||||
{
|
||||
if (double.IsNaN(value))
|
||||
@@ -237,11 +182,6 @@ namespace ARMeilleure.Instructions
|
||||
#endregion
|
||||
|
||||
#region "Count"
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static ulong CountLeadingSigns(ulong value, int size) // size is 8, 16, 32 or 64 (SIMD&FP or Base Inst.).
|
||||
{
|
||||
value ^= value >> 1;
|
||||
@@ -261,11 +201,6 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
private static ReadOnlySpan<byte> ClzNibbleTbl => [4, 3, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static ulong CountLeadingZeros(ulong value, int size) // size is 8, 16, 32 or 64 (SIMD&FP or Base Inst.).
|
||||
{
|
||||
if (value == 0ul)
|
||||
@@ -289,81 +224,41 @@ namespace ARMeilleure.Instructions
|
||||
#endregion
|
||||
|
||||
#region "Table"
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static V128 Tbl1(V128 vector, int bytes, V128 tb0)
|
||||
{
|
||||
return TblOrTbx(default, vector, bytes, tb0);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static V128 Tbl2(V128 vector, int bytes, V128 tb0, V128 tb1)
|
||||
{
|
||||
return TblOrTbx(default, vector, bytes, tb0, tb1);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static V128 Tbl3(V128 vector, int bytes, V128 tb0, V128 tb1, V128 tb2)
|
||||
{
|
||||
return TblOrTbx(default, vector, bytes, tb0, tb1, tb2);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
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);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static V128 Tbx1(V128 dest, V128 vector, int bytes, V128 tb0)
|
||||
{
|
||||
return TblOrTbx(dest, vector, bytes, tb0);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static V128 Tbx2(V128 dest, V128 vector, int bytes, V128 tb0, V128 tb1)
|
||||
{
|
||||
return TblOrTbx(dest, vector, bytes, tb0, tb1);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static V128 Tbx3(V128 dest, V128 vector, int bytes, V128 tb0, V128 tb1, V128 tb2)
|
||||
{
|
||||
return TblOrTbx(dest, vector, bytes, tb0, tb1, tb2);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
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);
|
||||
@@ -405,54 +300,14 @@ namespace ARMeilleure.Instructions
|
||||
private const uint Crc32RevPoly = 0xedb88320;
|
||||
private const uint Crc32cRevPoly = 0x82f63b78;
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static uint Crc32b(uint crc, byte value) => Crc32(crc, Crc32RevPoly, value);
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static uint Crc32h(uint crc, ushort value) => Crc32h(crc, Crc32RevPoly, value);
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static uint Crc32w(uint crc, uint value) => Crc32w(crc, Crc32RevPoly, value);
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static uint Crc32x(uint crc, ulong value) => Crc32x(crc, Crc32RevPoly, value);
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static uint Crc32cb(uint crc, byte value) => Crc32(crc, Crc32cRevPoly, value);
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static uint Crc32ch(uint crc, ushort value) => Crc32h(crc, Crc32cRevPoly, value);
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static uint Crc32cw(uint crc, uint value) => Crc32w(crc, Crc32cRevPoly, value);
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static uint Crc32cx(uint crc, ulong value) => Crc32x(crc, Crc32cRevPoly, value);
|
||||
|
||||
private static uint Crc32h(uint crc, uint poly, ushort val)
|
||||
@@ -503,41 +358,21 @@ namespace ARMeilleure.Instructions
|
||||
#endregion
|
||||
|
||||
#region "Aes"
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static V128 Decrypt(V128 value, V128 roundKey)
|
||||
{
|
||||
return CryptoHelper.AesInvSubBytes(CryptoHelper.AesInvShiftRows(value ^ roundKey));
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static V128 Encrypt(V128 value, V128 roundKey)
|
||||
{
|
||||
return CryptoHelper.AesSubBytes(CryptoHelper.AesShiftRows(value ^ roundKey));
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static V128 InverseMixColumns(V128 value)
|
||||
{
|
||||
return CryptoHelper.AesInvMixColumns(value);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static V128 MixColumns(V128 value)
|
||||
{
|
||||
return CryptoHelper.AesMixColumns(value);
|
||||
@@ -545,11 +380,6 @@ namespace ARMeilleure.Instructions
|
||||
#endregion
|
||||
|
||||
#region "Sha1"
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static V128 HashChoose(V128 hash_abcd, uint hash_e, V128 wk)
|
||||
{
|
||||
for (int e = 0; e <= 3; e++)
|
||||
@@ -570,21 +400,11 @@ namespace ARMeilleure.Instructions
|
||||
return hash_abcd;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static uint FixedRotate(uint hash_e)
|
||||
{
|
||||
return hash_e.Rol(30);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static V128 HashMajority(V128 hash_abcd, uint hash_e, V128 wk)
|
||||
{
|
||||
for (int e = 0; e <= 3; e++)
|
||||
@@ -605,11 +425,6 @@ namespace ARMeilleure.Instructions
|
||||
return hash_abcd;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static V128 HashParity(V128 hash_abcd, uint hash_e, V128 wk)
|
||||
{
|
||||
for (int e = 0; e <= 3; e++)
|
||||
@@ -630,11 +445,6 @@ namespace ARMeilleure.Instructions
|
||||
return hash_abcd;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static V128 Sha1SchedulePart1(V128 w0_3, V128 w4_7, V128 w8_11)
|
||||
{
|
||||
ulong t2 = w4_7.Extract<ulong>(0);
|
||||
@@ -645,11 +455,6 @@ namespace ARMeilleure.Instructions
|
||||
return result ^ (w0_3 ^ w8_11);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static V128 Sha1SchedulePart2(V128 tw0_3, V128 w12_15)
|
||||
{
|
||||
V128 t = tw0_3 ^ (w12_15 >> 32);
|
||||
@@ -694,31 +499,16 @@ namespace ARMeilleure.Instructions
|
||||
#endregion
|
||||
|
||||
#region "Sha256"
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static V128 HashLower(V128 hash_abcd, V128 hash_efgh, V128 wk)
|
||||
{
|
||||
return Sha256Hash(hash_abcd, hash_efgh, wk, part1: true);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static V128 HashUpper(V128 hash_abcd, V128 hash_efgh, V128 wk)
|
||||
{
|
||||
return Sha256Hash(hash_abcd, hash_efgh, wk, part1: false);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static V128 Sha256SchedulePart1(V128 w0_3, V128 w4_7)
|
||||
{
|
||||
V128 result = new();
|
||||
@@ -737,11 +527,6 @@ namespace ARMeilleure.Instructions
|
||||
return result;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static V128 Sha256SchedulePart2(V128 w0_3, V128 w8_11, V128 w12_15)
|
||||
{
|
||||
V128 result = new();
|
||||
@@ -843,11 +628,6 @@ namespace ARMeilleure.Instructions
|
||||
}
|
||||
#endregion
|
||||
|
||||
#if ANDROID
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#else
|
||||
[UnmanagedCallersOnly]
|
||||
#endif
|
||||
public static V128 PolynomialMult64_128(ulong op1, ulong op2)
|
||||
{
|
||||
V128 result = V128.Zero;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,7 @@ using System;
|
||||
|
||||
namespace ARMeilleure.Memory
|
||||
{
|
||||
public class ReservedRegion
|
||||
public class ReservedRegion : IDisposable
|
||||
{
|
||||
public const int DefaultGranularity = 65536; // Mapping granularity in Windows.
|
||||
|
||||
|
||||
@@ -8,13 +8,11 @@ namespace ARMeilleure
|
||||
// low-core count PPTC
|
||||
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 AllowLcqInFunctionTable { get; set; } = true;
|
||||
public static bool UseUnmanagedDispatchLoop { get; set; } = true;
|
||||
public static bool EnableDebugging { get; set; } = false;
|
||||
public static bool EnableDeepCallRecursionProtection { get; set; } = true;
|
||||
|
||||
public static bool UseAdvSimdIfAvailable { get; set; } = true;
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace ARMeilleure.Signal
|
||||
{
|
||||
public static class NativeSignalHandlerGenerator
|
||||
{
|
||||
public const int MaxTrackedRanges = 8;
|
||||
public const int MaxTrackedRanges = 16;
|
||||
|
||||
private const int StructAddressOffset = 0;
|
||||
private const int StructWriteOffset = 4;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.Translation;
|
||||
using ARMeilleure.Translation.Cache;
|
||||
using System.Runtime.InteropServices;
|
||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||
|
||||
@@ -17,7 +18,7 @@ namespace ARMeilleure.Signal
|
||||
public delegate int DebugThreadLocalMapGetOrReserve(int threadId, int initialState);
|
||||
public delegate void DebugNativeWriteLoop(nint nativeWriteLoopPtr, nint writePtr);
|
||||
|
||||
public static DebugPartialUnmap GenerateDebugPartialUnmap()
|
||||
public static DebugPartialUnmap GenerateDebugPartialUnmap(JitCache jitCache)
|
||||
{
|
||||
EmitterContext context = new();
|
||||
|
||||
@@ -31,10 +32,10 @@ namespace ARMeilleure.Signal
|
||||
|
||||
OperandType[] argTypes = [OperandType.I64];
|
||||
|
||||
return Compiler.Compile(cfg, argTypes, OperandType.I32, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<DebugPartialUnmap>();
|
||||
return Compiler.Compile(cfg, argTypes, OperandType.I32, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<DebugPartialUnmap>(jitCache);
|
||||
}
|
||||
|
||||
public static DebugThreadLocalMapGetOrReserve GenerateDebugThreadLocalMapGetOrReserve(nint structPtr)
|
||||
public static DebugThreadLocalMapGetOrReserve GenerateDebugThreadLocalMapGetOrReserve(JitCache jitCache, nint structPtr)
|
||||
{
|
||||
EmitterContext context = new();
|
||||
|
||||
@@ -48,10 +49,10 @@ namespace ARMeilleure.Signal
|
||||
|
||||
OperandType[] argTypes = [OperandType.I64];
|
||||
|
||||
return Compiler.Compile(cfg, argTypes, OperandType.I32, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<DebugThreadLocalMapGetOrReserve>();
|
||||
return Compiler.Compile(cfg, argTypes, OperandType.I32, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<DebugThreadLocalMapGetOrReserve>(jitCache);
|
||||
}
|
||||
|
||||
public static DebugNativeWriteLoop GenerateDebugNativeWriteLoop()
|
||||
public static DebugNativeWriteLoop GenerateDebugNativeWriteLoop(JitCache jitCache)
|
||||
{
|
||||
EmitterContext context = new();
|
||||
|
||||
@@ -77,7 +78,7 @@ namespace ARMeilleure.Signal
|
||||
|
||||
OperandType[] argTypes = [OperandType.I64];
|
||||
|
||||
return Compiler.Compile(cfg, argTypes, OperandType.None, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<DebugNativeWriteLoop>();
|
||||
return Compiler.Compile(cfg, argTypes, OperandType.None, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<DebugNativeWriteLoop>(jitCache);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using ARMeilleure.Memory;
|
||||
using System.Threading;
|
||||
|
||||
namespace ARMeilleure.State
|
||||
{
|
||||
@@ -10,7 +11,7 @@ namespace ARMeilleure.State
|
||||
|
||||
internal nint NativeContextPtr => _nativeContext.BasePtr;
|
||||
|
||||
private bool _interrupted;
|
||||
internal bool Interrupted { get; private set; }
|
||||
|
||||
private readonly ICounter _counter;
|
||||
|
||||
@@ -67,6 +68,8 @@ namespace ARMeilleure.State
|
||||
|
||||
public bool IsAarch32 { get; set; }
|
||||
|
||||
public ulong ThreadUid { get; set; }
|
||||
|
||||
internal ExecutionMode ExecutionMode
|
||||
{
|
||||
get
|
||||
@@ -92,14 +95,19 @@ namespace ARMeilleure.State
|
||||
|
||||
private readonly ExceptionCallbackNoArgs _interruptCallback;
|
||||
private readonly ExceptionCallback _breakCallback;
|
||||
private readonly ExceptionCallbackNoArgs _stepCallback;
|
||||
private readonly ExceptionCallback _supervisorCallback;
|
||||
private readonly ExceptionCallback _undefinedCallback;
|
||||
|
||||
internal int ShouldStep;
|
||||
public ulong DebugPc { get; set; }
|
||||
|
||||
public ExecutionContext(
|
||||
IJitMemoryAllocator allocator,
|
||||
ICounter counter,
|
||||
ExceptionCallbackNoArgs interruptCallback = null,
|
||||
ExceptionCallback breakCallback = null,
|
||||
ExceptionCallbackNoArgs stepCallback = null,
|
||||
ExceptionCallback supervisorCallback = null,
|
||||
ExceptionCallback undefinedCallback = null)
|
||||
{
|
||||
@@ -107,6 +115,7 @@ namespace ARMeilleure.State
|
||||
_counter = counter;
|
||||
_interruptCallback = interruptCallback;
|
||||
_breakCallback = breakCallback;
|
||||
_stepCallback = stepCallback;
|
||||
_supervisorCallback = supervisorCallback;
|
||||
_undefinedCallback = undefinedCallback;
|
||||
|
||||
@@ -134,9 +143,9 @@ namespace ARMeilleure.State
|
||||
|
||||
internal void CheckInterrupt()
|
||||
{
|
||||
if (_interrupted)
|
||||
if (Interrupted)
|
||||
{
|
||||
_interrupted = false;
|
||||
Interrupted = false;
|
||||
|
||||
_interruptCallback?.Invoke(this);
|
||||
}
|
||||
@@ -146,16 +155,37 @@ namespace ARMeilleure.State
|
||||
|
||||
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)
|
||||
{
|
||||
if (Optimizations.EnableDebugging)
|
||||
{
|
||||
DebugPc = Pc;
|
||||
}
|
||||
|
||||
_breakCallback?.Invoke(this, address, imm);
|
||||
}
|
||||
|
||||
internal void OnSupervisorCall(ulong address, int imm)
|
||||
{
|
||||
if (Optimizations.EnableDebugging)
|
||||
{
|
||||
DebugPc = Pc;
|
||||
}
|
||||
|
||||
_supervisorCallback?.Invoke(this, address, imm);
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,12 @@ namespace ARMeilleure.State
|
||||
public int Running;
|
||||
public int CallDepth;
|
||||
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();
|
||||
@@ -40,6 +46,11 @@ namespace ARMeilleure.State
|
||||
|
||||
public ulong GetPc()
|
||||
{
|
||||
if (Optimizations.EnableDebugging)
|
||||
{
|
||||
return GetStorage().DebugPrecisePc;
|
||||
}
|
||||
|
||||
// TODO: More precise tracking of PC value.
|
||||
return GetStorage().DispatchAddress;
|
||||
}
|
||||
@@ -269,6 +280,11 @@ namespace ARMeilleure.State
|
||||
return StorageOffset(ref _dummyStorage, ref _dummyStorage.Running);
|
||||
}
|
||||
|
||||
public static int GetDebugPrecisePcOffset()
|
||||
{
|
||||
return StorageOffset(ref _dummyStorage, ref _dummyStorage.DebugPrecisePc);
|
||||
}
|
||||
|
||||
public static int GetCallDepthOffset()
|
||||
{
|
||||
return StorageOffset(ref _dummyStorage, ref _dummyStorage.CallDepth);
|
||||
|
||||
@@ -52,6 +52,7 @@ namespace ARMeilleure.Translation
|
||||
public bool HighCq { get; }
|
||||
public bool HasPtc { get; }
|
||||
public Aarch32Mode Mode { get; }
|
||||
public bool IsSingleStep { get; }
|
||||
|
||||
private int _ifThenBlockStateIndex = 0;
|
||||
private Condition[] _ifThenBlockState = [];
|
||||
@@ -66,7 +67,8 @@ namespace ARMeilleure.Translation
|
||||
ulong entryAddress,
|
||||
bool highCq,
|
||||
bool hasPtc,
|
||||
Aarch32Mode mode)
|
||||
Aarch32Mode mode,
|
||||
bool isSingleStep)
|
||||
{
|
||||
Memory = memory;
|
||||
CountTable = countTable;
|
||||
@@ -76,6 +78,7 @@ namespace ARMeilleure.Translation
|
||||
HighCq = highCq;
|
||||
HasPtc = hasPtc;
|
||||
Mode = mode;
|
||||
IsSingleStep = isSingleStep;
|
||||
|
||||
_labels = new Dictionary<ulong, Operand>();
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace ARMeilleure.Translation.Cache
|
||||
{
|
||||
readonly struct CacheEntry : IComparable<CacheEntry>
|
||||
public readonly struct CacheEntry : IComparable<CacheEntry>
|
||||
{
|
||||
public int Offset { get; }
|
||||
public int Size { get; }
|
||||
|
||||
@@ -23,11 +23,30 @@ namespace ARMeilleure.Translation.Cache
|
||||
}
|
||||
}
|
||||
|
||||
private readonly int _regionSize;
|
||||
private int _regionCount;
|
||||
|
||||
private readonly List<MemoryBlock> _blocks = [];
|
||||
|
||||
public CacheMemoryAllocator(int capacity)
|
||||
public CacheMemoryAllocator(int regionSize, int initialRegionCount = 1)
|
||||
{
|
||||
_blocks.Add(new MemoryBlock(0, capacity));
|
||||
_regionCount = 0;
|
||||
_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)
|
||||
@@ -66,12 +85,13 @@ namespace ARMeilleure.Translation.Cache
|
||||
index = ~index;
|
||||
}
|
||||
|
||||
if (index < _blocks.Count)
|
||||
int endOffs = block.Offset + block.Size;
|
||||
|
||||
// Don't merge blocks from different allocations
|
||||
if (index < _blocks.Count && endOffs % _regionSize != 0)
|
||||
{
|
||||
MemoryBlock next = _blocks[index];
|
||||
|
||||
int endOffs = block.Offset + block.Size;
|
||||
|
||||
if (next.Offset == endOffs)
|
||||
{
|
||||
block = new MemoryBlock(block.Offset, block.Size + next.Size);
|
||||
@@ -79,7 +99,8 @@ namespace ARMeilleure.Translation.Cache
|
||||
}
|
||||
}
|
||||
|
||||
if (index > 0)
|
||||
// Don't merge blocks from different allocations
|
||||
if (index > 0 && block.Offset % _regionSize != 0)
|
||||
{
|
||||
MemoryBlock prev = _blocks[index - 1];
|
||||
|
||||
|
||||
@@ -2,140 +2,70 @@ using ARMeilleure.CodeGen;
|
||||
using ARMeilleure.CodeGen.Unwinding;
|
||||
using ARMeilleure.Memory;
|
||||
using ARMeilleure.Native;
|
||||
using Humanizer;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Memory;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Versioning;
|
||||
using System.Threading;
|
||||
|
||||
namespace ARMeilleure.Translation.Cache
|
||||
{
|
||||
static partial class JitCache
|
||||
public partial class JitCache : IDisposable
|
||||
{
|
||||
private static readonly int _pageSize = (int)MemoryBlock.GetPageSize();
|
||||
private static readonly int _pageMask = _pageSize - 1;
|
||||
|
||||
private const int CodeAlignment = 4;
|
||||
private const int FullCacheSize = 2047 * 1024 * 1024;
|
||||
private const int ReducedCacheSize = FullCacheSize / 8;
|
||||
private const int CodeAlignment = 4; // Bytes.
|
||||
private const uint CacheSize = 256 * 1024 * 1024;
|
||||
|
||||
private const float EvictionTargetPercentage = 0.20f;
|
||||
private const int MaxEntriesToEvictAtOnce = 100;
|
||||
private readonly JitCacheInvalidation _jitCacheInvalidator;
|
||||
|
||||
// Simple logging configuration
|
||||
private const int LogInterval = 5000; // Log every 5000 allocations
|
||||
private readonly CacheMemoryAllocator _cacheAllocator;
|
||||
|
||||
private static ReservedRegion _jitRegion;
|
||||
private static JitCacheInvalidation _jitCacheInvalidator;
|
||||
private readonly List<CacheEntry> _cacheEntries = [];
|
||||
|
||||
private static CacheMemoryAllocator _cacheAllocator;
|
||||
private readonly Lock _lock = new();
|
||||
|
||||
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++;
|
||||
}
|
||||
}
|
||||
private readonly List<ReservedRegion> _jitRegions = [];
|
||||
|
||||
[SupportedOSPlatform("windows")]
|
||||
[LibraryImport("kernel32.dll", SetLastError = true)]
|
||||
public static partial nint FlushInstructionCache(nint hProcess, nint lpAddress, nuint dwSize);
|
||||
private static partial nint FlushInstructionCache(nint hProcess, nint lpAddress, nuint dwSize);
|
||||
|
||||
public static void Initialize(IJitMemoryAllocator allocator)
|
||||
public JitCache(IJitMemoryAllocator allocator)
|
||||
{
|
||||
if (_initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
if (_initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_jitRegions.Add(new(allocator, CacheSize));
|
||||
|
||||
_cacheSize = Optimizations.CacheEviction ? ReducedCacheSize : FullCacheSize;
|
||||
_jitRegion = new ReservedRegion(allocator, (ulong)_cacheSize);
|
||||
_cacheAllocator = new((int)CacheSize);
|
||||
|
||||
if (!OperatingSystem.IsWindows() && !OperatingSystem.IsMacOS())
|
||||
{
|
||||
_jitCacheInvalidator = new JitCacheInvalidation(allocator);
|
||||
}
|
||||
|
||||
_cacheAllocator = new CacheMemoryAllocator(_cacheSize);
|
||||
|
||||
if (OperatingSystem.IsWindows())
|
||||
{
|
||||
JitUnwindWindows.InstallFunctionTableHandler(_jitRegion.Pointer, (uint)_cacheSize, _jitRegion.Pointer + Allocate(_pageSize));
|
||||
JitUnwindWindows.InstallFunctionTableHandler(
|
||||
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 static nint Map(CompiledFunction func)
|
||||
public nint Map(CompiledFunction func)
|
||||
{
|
||||
byte[] code = func.Code;
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
Debug.Assert(_initialized);
|
||||
_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;
|
||||
int funcOffset = Allocate(code.Length);
|
||||
nint funcPtr = GetFunctionPtr(funcOffset);
|
||||
|
||||
if (OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
|
||||
{
|
||||
@@ -165,79 +95,104 @@ namespace ARMeilleure.Translation.Cache
|
||||
|
||||
Add(funcOffset, code.Length, func.UnwindInfo);
|
||||
|
||||
// Simple periodic logging
|
||||
if (_totalAllocations % LogInterval == 0)
|
||||
{
|
||||
LogCacheStatus();
|
||||
}
|
||||
|
||||
return funcPtr;
|
||||
}
|
||||
}
|
||||
|
||||
public static void Unmap(nint pointer)
|
||||
public void Unmap(nint pointer)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
Debug.Assert(_initialized);
|
||||
|
||||
int funcOffset = (int)(pointer.ToInt64() - _jitRegion.Pointer.ToInt64());
|
||||
|
||||
if (TryFind(funcOffset, out CacheEntry entry, out int entryIndex) && entry.Offset == funcOffset)
|
||||
for (int i = 0; i < _jitRegions.Count; i++)
|
||||
{
|
||||
_cacheAllocator.Free(funcOffset, AlignCodeSize(entry.Size));
|
||||
_cacheEntries.RemoveAt(entryIndex);
|
||||
|
||||
if (Optimizations.CacheEviction)
|
||||
ReservedRegion jitRegion = _jitRegions[i];
|
||||
if (pointer.ToInt64() < jitRegion.Pointer.ToInt64() ||
|
||||
pointer.ToInt64() >= (jitRegion.Pointer + (nint)CacheSize).ToInt64())
|
||||
{
|
||||
_entryUsageStats.Remove(funcOffset);
|
||||
continue;
|
||||
}
|
||||
|
||||
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 static void ReprotectAsWritable(int offset, int size)
|
||||
private void ReprotectAsWritable(int offset, int size)
|
||||
{
|
||||
int endOffs = offset + size;
|
||||
|
||||
int regionStart = offset & ~_pageMask;
|
||||
int regionEnd = (endOffs + _pageMask) & ~_pageMask;
|
||||
|
||||
_jitRegion.Block.MapAsRwx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
||||
int regionStart = (offset % (int)CacheSize) & ~_pageMask;
|
||||
int regionEnd = endOffs % (int)CacheSize == 0
|
||||
? (((int)CacheSize) + _pageMask) & ~_pageMask
|
||||
: ((endOffs % (int)CacheSize) + _pageMask) & ~_pageMask;
|
||||
GetRegion(offset).Block.MapAsRwx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
||||
}
|
||||
|
||||
private static void ReprotectAsExecutable(int offset, int size)
|
||||
private void ReprotectAsExecutable(int offset, int 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;
|
||||
|
||||
int regionStart = offset & ~_pageMask;
|
||||
int regionEnd = (endOffs + _pageMask) & ~_pageMask;
|
||||
|
||||
_jitRegion.Block.MapAsRx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
||||
GetRegion(offset).Block.MapAsRx((ulong)regionStart, (ulong)(regionEnd - regionStart));
|
||||
}
|
||||
|
||||
private static int Allocate(int codeSize)
|
||||
private int Allocate(int codeSize)
|
||||
{
|
||||
codeSize = AlignCodeSize(codeSize);
|
||||
|
||||
int allocOffset = _cacheAllocator.Allocate(codeSize);
|
||||
|
||||
if (allocOffset < 0)
|
||||
if (allocOffset >= 0)
|
||||
{
|
||||
throw new OutOfMemoryException("JIT Cache exhausted.");
|
||||
GetRegion(allocOffset).ExpandIfNeeded((ulong)(allocOffset % (int)CacheSize) + (ulong)codeSize);
|
||||
return allocOffset;
|
||||
}
|
||||
|
||||
_jitRegion.ExpandIfNeeded((ulong)allocOffset + (ulong)codeSize);
|
||||
_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)
|
||||
{
|
||||
throw new OutOfMemoryException("Failed to allocate in new Cache Region!");
|
||||
}
|
||||
|
||||
GetRegion(allocOffset).ExpandIfNeeded((ulong)(allocOffset % (int)CacheSize) + (ulong)codeSize);
|
||||
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)
|
||||
{
|
||||
return checked(codeSize + (CodeAlignment - 1)) & ~(CodeAlignment - 1);
|
||||
}
|
||||
|
||||
private static void Add(int offset, int size, UnwindInfo unwindInfo)
|
||||
private void Add(int offset, int size, UnwindInfo unwindInfo)
|
||||
{
|
||||
CacheEntry entry = new(offset, size, unwindInfo);
|
||||
|
||||
@@ -249,14 +204,9 @@ namespace ARMeilleure.Translation.Cache
|
||||
}
|
||||
|
||||
_cacheEntries.Insert(index, entry);
|
||||
|
||||
if (Optimizations.CacheEviction)
|
||||
{
|
||||
_entryUsageStats[offset] = new EntryUsageStats();
|
||||
}
|
||||
}
|
||||
|
||||
public static bool TryFind(int offset, out CacheEntry entry, out int entryIndex)
|
||||
public bool TryFind(int offset, out CacheEntry entry, out int entryIndex)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
@@ -270,12 +220,6 @@ namespace ARMeilleure.Translation.Cache
|
||||
if (index >= 0)
|
||||
{
|
||||
entry = _cacheEntries[index];
|
||||
|
||||
if (Optimizations.CacheEviction && _entryUsageStats.TryGetValue(offset, out EntryUsageStats stats))
|
||||
{
|
||||
stats.UpdateUsage();
|
||||
}
|
||||
|
||||
entryIndex = index;
|
||||
return true;
|
||||
}
|
||||
@@ -286,82 +230,17 @@ namespace ARMeilleure.Translation.Cache
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void EvictEntries(int requiredSize)
|
||||
public void Dispose()
|
||||
{
|
||||
if (!Optimizations.CacheEviction)
|
||||
if (OperatingSystem.IsWindows())
|
||||
{
|
||||
return;
|
||||
JitUnwindWindows.RemoveFunctionTableHandler(_jitRegions[0].Pointer);
|
||||
}
|
||||
|
||||
lock (_lock)
|
||||
foreach (ReservedRegion jitRegion in _jitRegions)
|
||||
{
|
||||
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");
|
||||
jitRegion.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
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,6 +2,8 @@
|
||||
|
||||
using ARMeilleure.CodeGen.Unwinding;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
@@ -27,6 +29,29 @@ namespace ARMeilleure.Translation.Cache
|
||||
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
|
||||
{
|
||||
PushNonvol = 0,
|
||||
@@ -59,27 +84,28 @@ namespace ARMeilleure.Translation.Cache
|
||||
|
||||
private static GetRuntimeFunctionCallback _getRuntimeFunctionCallback;
|
||||
|
||||
private static int _sizeOfRuntimeFunction;
|
||||
private static readonly int _sizeOfRuntimeFunction;
|
||||
|
||||
private unsafe static RuntimeFunction* _runtimeFunction;
|
||||
private static readonly ConcurrentDictionary<ulong, InternalFunctionHandler> _functionTableHandlers = new();
|
||||
|
||||
private unsafe static UnwindInfo* _unwindInfo;
|
||||
static JitUnwindWindows()
|
||||
{
|
||||
_sizeOfRuntimeFunction = Marshal.SizeOf<RuntimeFunction>();
|
||||
}
|
||||
|
||||
public static void InstallFunctionTableHandler(nint codeCachePointer, uint codeCacheLength, nint workBufferPtr)
|
||||
public static void InstallFunctionTableHandler(JitCache jitCache, nint codeCachePointer, uint codeCacheLength, nint workBufferPtr)
|
||||
{
|
||||
ulong codeCachePtr = (ulong)codeCachePointer.ToInt64();
|
||||
|
||||
_sizeOfRuntimeFunction = Marshal.SizeOf<RuntimeFunction>();
|
||||
|
||||
bool result;
|
||||
|
||||
InternalFunctionHandler handler;
|
||||
|
||||
unsafe
|
||||
{
|
||||
_runtimeFunction = (RuntimeFunction*)workBufferPtr;
|
||||
handler = new InternalFunctionHandler(jitCache, workBufferPtr);
|
||||
|
||||
_unwindInfo = (UnwindInfo*)(workBufferPtr + _sizeOfRuntimeFunction);
|
||||
|
||||
_getRuntimeFunctionCallback = new GetRuntimeFunctionCallback(FunctionTableHandler);
|
||||
_getRuntimeFunctionCallback = handler.FunctionTableHandler;
|
||||
|
||||
result = RtlInstallFunctionTableCallback(
|
||||
codeCachePtr | 3,
|
||||
@@ -94,6 +120,8 @@ namespace ARMeilleure.Translation.Cache
|
||||
{
|
||||
throw new InvalidOperationException("Failure installing function table callback.");
|
||||
}
|
||||
|
||||
_functionTableHandlers.TryAdd(codeCachePtr, handler);
|
||||
}
|
||||
|
||||
public static void RemoveFunctionTableHandler(nint codeCachePointer)
|
||||
@@ -111,24 +139,26 @@ namespace ARMeilleure.Translation.Cache
|
||||
{
|
||||
throw new InvalidOperationException("Failure removing function table callback.");
|
||||
}
|
||||
|
||||
_functionTableHandlers.Remove(codeCachePtr, out _);
|
||||
}
|
||||
|
||||
private static unsafe RuntimeFunction* FunctionTableHandler(ulong controlPc, nint context)
|
||||
private static unsafe RuntimeFunction* FunctionTableHandler(JitCache jitCache, RuntimeFunction* runtimeFunction, UnwindInfo* unwindInfo, ulong controlPc, nint context)
|
||||
{
|
||||
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.
|
||||
}
|
||||
|
||||
CodeGen.Unwinding.UnwindInfo unwindInfo = funcEntry.UnwindInfo;
|
||||
CodeGen.Unwinding.UnwindInfo funcUnwindInfo = funcEntry.UnwindInfo;
|
||||
|
||||
int codeIndex = 0;
|
||||
|
||||
for (int index = unwindInfo.PushEntries.Length - 1; index >= 0; index--)
|
||||
for (int index = funcUnwindInfo.PushEntries.Length - 1; index >= 0; index--)
|
||||
{
|
||||
UnwindPushEntry entry = unwindInfo.PushEntries[index];
|
||||
UnwindPushEntry entry = funcUnwindInfo.PushEntries[index];
|
||||
|
||||
switch (entry.PseudoOp)
|
||||
{
|
||||
@@ -140,14 +170,14 @@ namespace ARMeilleure.Translation.Cache
|
||||
|
||||
if (stackOffset <= 0xFFFF0)
|
||||
{
|
||||
_unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOp.SaveXmm128, entry.PrologOffset, entry.RegIndex);
|
||||
_unwindInfo->UnwindCodes[codeIndex++] = (ushort)(stackOffset / 16);
|
||||
unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOp.SaveXmm128, entry.PrologOffset, entry.RegIndex);
|
||||
unwindInfo->UnwindCodes[codeIndex++] = (ushort)(stackOffset / 16);
|
||||
}
|
||||
else
|
||||
{
|
||||
_unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOp.SaveXmm128Far, entry.PrologOffset, entry.RegIndex);
|
||||
_unwindInfo->UnwindCodes[codeIndex++] = (ushort)(stackOffset >> 0);
|
||||
_unwindInfo->UnwindCodes[codeIndex++] = (ushort)(stackOffset >> 16);
|
||||
unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOp.SaveXmm128Far, entry.PrologOffset, entry.RegIndex);
|
||||
unwindInfo->UnwindCodes[codeIndex++] = (ushort)(stackOffset >> 0);
|
||||
unwindInfo->UnwindCodes[codeIndex++] = (ushort)(stackOffset >> 16);
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -161,18 +191,18 @@ namespace ARMeilleure.Translation.Cache
|
||||
|
||||
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)
|
||||
{
|
||||
_unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOp.AllocLarge, entry.PrologOffset, 0);
|
||||
_unwindInfo->UnwindCodes[codeIndex++] = (ushort)(allocSize / 8);
|
||||
unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOp.AllocLarge, entry.PrologOffset, 0);
|
||||
unwindInfo->UnwindCodes[codeIndex++] = (ushort)(allocSize / 8);
|
||||
}
|
||||
else
|
||||
{
|
||||
_unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOp.AllocLarge, entry.PrologOffset, 1);
|
||||
_unwindInfo->UnwindCodes[codeIndex++] = (ushort)(allocSize >> 0);
|
||||
_unwindInfo->UnwindCodes[codeIndex++] = (ushort)(allocSize >> 16);
|
||||
unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOp.AllocLarge, entry.PrologOffset, 1);
|
||||
unwindInfo->UnwindCodes[codeIndex++] = (ushort)(allocSize >> 0);
|
||||
unwindInfo->UnwindCodes[codeIndex++] = (ushort)(allocSize >> 16);
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -180,7 +210,7 @@ namespace ARMeilleure.Translation.Cache
|
||||
|
||||
case UnwindPseudoOp.PushReg:
|
||||
{
|
||||
_unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOp.PushNonvol, entry.PrologOffset, entry.RegIndex);
|
||||
unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOp.PushNonvol, entry.PrologOffset, entry.RegIndex);
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -192,16 +222,16 @@ namespace ARMeilleure.Translation.Cache
|
||||
|
||||
Debug.Assert(codeIndex <= MaxUnwindCodesArraySize);
|
||||
|
||||
_unwindInfo->VersionAndFlags = 1; // Flags: The function has no handler.
|
||||
_unwindInfo->SizeOfProlog = (byte)unwindInfo.PrologSize;
|
||||
_unwindInfo->CountOfUnwindCodes = (byte)codeIndex;
|
||||
_unwindInfo->FrameRegister = 0;
|
||||
unwindInfo->VersionAndFlags = 1; // Flags: The function has no handler.
|
||||
unwindInfo->SizeOfProlog = (byte)funcUnwindInfo.PrologSize;
|
||||
unwindInfo->CountOfUnwindCodes = (byte)codeIndex;
|
||||
unwindInfo->FrameRegister = 0;
|
||||
|
||||
_runtimeFunction->BeginAddress = (uint)funcEntry.Offset;
|
||||
_runtimeFunction->EndAddress = (uint)(funcEntry.Offset + funcEntry.Size);
|
||||
_runtimeFunction->UnwindData = (uint)_sizeOfRuntimeFunction;
|
||||
runtimeFunction->BeginAddress = (uint)funcEntry.Offset;
|
||||
runtimeFunction->EndAddress = (uint)(funcEntry.Offset + funcEntry.Size);
|
||||
runtimeFunction->UnwindData = (uint)_sizeOfRuntimeFunction;
|
||||
|
||||
return _runtimeFunction;
|
||||
return runtimeFunction;
|
||||
}
|
||||
|
||||
private static ushort PackUnwindOp(UnwindOp op, int prologOffset, int opInfo)
|
||||
|
||||
@@ -1,11 +1,18 @@
|
||||
using System;
|
||||
|
||||
namespace ARMeilleure.Translation
|
||||
{
|
||||
public class DelegateInfo
|
||||
class DelegateInfo
|
||||
{
|
||||
#pragma warning disable IDE0052 // Remove unread private member
|
||||
private readonly Delegate _dlg; // Ensure that this delegate will not be garbage collected.
|
||||
#pragma warning restore IDE0052
|
||||
|
||||
public nint FuncPtr { get; }
|
||||
|
||||
public DelegateInfo(nint funcPtr)
|
||||
public DelegateInfo(Delegate dlg, nint funcPtr)
|
||||
{
|
||||
_dlg = dlg;
|
||||
FuncPtr = funcPtr;
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -97,14 +97,7 @@ namespace ARMeilleure.Translation
|
||||
|
||||
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();
|
||||
#endif
|
||||
nint funcPtr = Delegates.GetDelegateFuncPtr(info);
|
||||
|
||||
OperandType returnType = GetOperandType(info.ReturnType);
|
||||
|
||||
|
||||
@@ -359,10 +359,7 @@ namespace ARMeilleure.Translation
|
||||
|
||||
IntervalTreeNode<TK, TV> tmp = LeftOf(replacementNode) ?? RightOf(replacementNode);
|
||||
|
||||
if (tmp != null)
|
||||
{
|
||||
tmp.Parent = ParentOf(replacementNode);
|
||||
}
|
||||
tmp?.Parent = ParentOf(replacementNode);
|
||||
|
||||
if (ParentOf(replacementNode) == null)
|
||||
{
|
||||
@@ -570,10 +567,7 @@ namespace ARMeilleure.Translation
|
||||
{
|
||||
IntervalTreeNode<TK, TV> right = RightOf(node);
|
||||
node.Right = LeftOf(right);
|
||||
if (node.Right != null)
|
||||
{
|
||||
node.Right.Parent = node;
|
||||
}
|
||||
node.Right?.Parent = node;
|
||||
IntervalTreeNode<TK, TV> nodeParent = ParentOf(node);
|
||||
right.Parent = nodeParent;
|
||||
if (nodeParent == null)
|
||||
@@ -601,10 +595,7 @@ namespace ARMeilleure.Translation
|
||||
{
|
||||
IntervalTreeNode<TK, TV> left = LeftOf(node);
|
||||
node.Left = RightOf(left);
|
||||
if (node.Left != null)
|
||||
{
|
||||
node.Left.Parent = node;
|
||||
}
|
||||
node.Left?.Parent = node;
|
||||
IntervalTreeNode<TK, TV> nodeParent = ParentOf(node);
|
||||
left.Parent = nodeParent;
|
||||
if (nodeParent == null)
|
||||
@@ -651,10 +642,7 @@ namespace ARMeilleure.Translation
|
||||
/// <param name="color">Color (Boolean)</param>
|
||||
private static void SetColor(IntervalTreeNode<TK, TV> node, bool color)
|
||||
{
|
||||
if (node != null)
|
||||
{
|
||||
node.Color = color;
|
||||
}
|
||||
node?.Color = color;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -4,6 +4,7 @@ using ARMeilleure.CodeGen.Unwinding;
|
||||
using ARMeilleure.Common;
|
||||
using ARMeilleure.Memory;
|
||||
using ARMeilleure.State;
|
||||
using ARMeilleure.Translation.Cache;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Configuration;
|
||||
using Ryujinx.Common.Logging;
|
||||
@@ -46,6 +47,8 @@ namespace ARMeilleure.Translation.PTC
|
||||
|
||||
public PtcProfiler Profiler { get; }
|
||||
|
||||
private readonly JitCache _jitCache;
|
||||
|
||||
// Carriers.
|
||||
private MemoryStream _infosStream;
|
||||
private List<byte[]> _codesList;
|
||||
@@ -75,7 +78,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
private volatile int _translateTotalCount;
|
||||
public event Action<PtcLoadingState, int, int> PtcStateChanged;
|
||||
|
||||
public Ptc()
|
||||
public Ptc(JitCache jitCache)
|
||||
{
|
||||
Profiler = new PtcProfiler(this);
|
||||
|
||||
@@ -86,6 +89,8 @@ namespace ARMeilleure.Translation.PTC
|
||||
|
||||
_waitEvent = new ManualResetEvent(true);
|
||||
|
||||
_jitCache = jitCache;
|
||||
|
||||
_disposed = false;
|
||||
|
||||
CacheInfo = new PtcCacheInfo(0, null, null, 0, null, "Unknown", "default");
|
||||
@@ -253,7 +258,8 @@ namespace ARMeilleure.Translation.PTC
|
||||
outerHeader.FeatureInfo != GetFeatureInfo() ||
|
||||
outerHeader.MemoryManagerMode != GetMemoryManagerMode() ||
|
||||
outerHeader.OSPlatform != GetOSPlatform() ||
|
||||
outerHeader.Architecture != (uint)RuntimeInformation.ProcessArchitecture)
|
||||
outerHeader.Architecture != (uint)RuntimeInformation.ProcessArchitecture ||
|
||||
outerHeader.DebuggerMode != Optimizations.EnableDebugging)
|
||||
{
|
||||
InvalidateCompressedStream(compressedStream);
|
||||
|
||||
@@ -434,6 +440,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
MemoryManagerMode = GetMemoryManagerMode(),
|
||||
OSPlatform = GetOSPlatform(),
|
||||
Architecture = (uint)RuntimeInformation.ProcessArchitecture,
|
||||
DebuggerMode = Optimizations.EnableDebugging,
|
||||
|
||||
UncompressedStreamSize =
|
||||
(long)Unsafe.SizeOf<InnerHeader>() +
|
||||
@@ -731,7 +738,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
return new UnwindInfo(pushEntries, prologueSize);
|
||||
}
|
||||
|
||||
private static TranslatedFunction FastTranslate(
|
||||
private TranslatedFunction FastTranslate(
|
||||
byte[] code,
|
||||
Counter<uint> callCounter,
|
||||
ulong guestSize,
|
||||
@@ -739,7 +746,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
bool highCq)
|
||||
{
|
||||
CompiledFunction cFunc = new(code, unwindInfo, RelocInfo.Empty);
|
||||
GuestFunction gFunc = cFunc.MapWithPointer<GuestFunction>(out nint gFuncPointer);
|
||||
GuestFunction gFunc = cFunc.MapWithPointer<GuestFunction>(_jitCache, out nint gFuncPointer);
|
||||
|
||||
return new TranslatedFunction(gFunc, gFuncPointer, callCounter, guestSize, highCq);
|
||||
}
|
||||
@@ -1030,7 +1037,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
return osPlatform;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 86*/)]
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 87*/)]
|
||||
private struct OuterHeader
|
||||
{
|
||||
public ulong Magic;
|
||||
@@ -1042,6 +1049,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
public byte MemoryManagerMode;
|
||||
public uint OSPlatform;
|
||||
public uint Architecture;
|
||||
public bool DebuggerMode;
|
||||
|
||||
public long UncompressedStreamSize;
|
||||
|
||||
|
||||
@@ -9,13 +9,13 @@ using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Timers;
|
||||
using static ARMeilleure.Translation.PTC.PtcFormatter;
|
||||
using Timer = System.Timers.Timer;
|
||||
using System.Linq;
|
||||
|
||||
namespace ARMeilleure.Translation.PTC
|
||||
{
|
||||
|
||||
@@ -24,10 +24,11 @@ namespace ARMeilleure.Translation
|
||||
private readonly IJitMemoryAllocator _allocator;
|
||||
private readonly ConcurrentQueue<KeyValuePair<ulong, TranslatedFunction>> _oldFuncs;
|
||||
|
||||
public readonly JitCache JitCache;
|
||||
private readonly Ptc _ptc;
|
||||
|
||||
internal TranslatorCache<TranslatedFunction> Functions { get; }
|
||||
internal IAddressTable<ulong> FunctionTable { get; }
|
||||
public IAddressTable<ulong> FunctionTable { get; }
|
||||
internal EntryTable<uint> CountTable { get; }
|
||||
internal TranslatorStubs Stubs { get; }
|
||||
internal TranslatorQueue Queue { get; }
|
||||
@@ -43,16 +44,19 @@ namespace ARMeilleure.Translation
|
||||
|
||||
_oldFuncs = new ConcurrentQueue<KeyValuePair<ulong, TranslatedFunction>>();
|
||||
|
||||
_ptc = new Ptc();
|
||||
JitCache = new JitCache(allocator);
|
||||
|
||||
_ptc = new Ptc(JitCache);
|
||||
|
||||
Queue = new TranslatorQueue();
|
||||
|
||||
JitCache.Initialize(allocator);
|
||||
|
||||
CountTable = new EntryTable<uint>();
|
||||
Functions = new TranslatorCache<TranslatedFunction>();
|
||||
|
||||
FunctionTable = functionTable;
|
||||
Stubs = new TranslatorStubs(FunctionTable);
|
||||
|
||||
Stubs = new TranslatorStubs(JitCache, FunctionTable);
|
||||
|
||||
FunctionTable.Fill = (ulong)Stubs.SlowDispatchStub;
|
||||
}
|
||||
@@ -119,7 +123,25 @@ namespace ARMeilleure.Translation
|
||||
|
||||
NativeInterface.RegisterThread(context, Memory, this);
|
||||
|
||||
if (Optimizations.UseUnmanagedDispatchLoop)
|
||||
if (Optimizations.EnableDebugging)
|
||||
{
|
||||
context.DebugPc = address;
|
||||
do
|
||||
{
|
||||
if (Interlocked.CompareExchange(ref context.ShouldStep, 0, 1) == 1)
|
||||
{
|
||||
context.DebugPc = Step(context, context.DebugPc);
|
||||
context.StepHandler();
|
||||
}
|
||||
else
|
||||
{
|
||||
context.DebugPc = ExecuteSingle(context, context.DebugPc);
|
||||
}
|
||||
context.CheckInterrupt();
|
||||
}
|
||||
while (context.Running && context.DebugPc != 0);
|
||||
}
|
||||
else if (Optimizations.UseUnmanagedDispatchLoop)
|
||||
{
|
||||
Stubs.DispatchLoop(context.NativeContextPtr, address);
|
||||
}
|
||||
@@ -149,6 +171,7 @@ namespace ARMeilleure.Translation
|
||||
}
|
||||
|
||||
ClearJitCache();
|
||||
JitCache.Dispose();
|
||||
|
||||
Stubs.Dispose();
|
||||
FunctionTable.Dispose();
|
||||
@@ -176,7 +199,7 @@ namespace ARMeilleure.Translation
|
||||
return nextAddr;
|
||||
}
|
||||
|
||||
public ulong Step(State.ExecutionContext context, ulong address)
|
||||
private ulong Step(State.ExecutionContext context, ulong address)
|
||||
{
|
||||
TranslatedFunction func = Translate(address, context.ExecutionMode, highCq: false, singleStep: true);
|
||||
|
||||
@@ -187,6 +210,8 @@ namespace ARMeilleure.Translation
|
||||
return address;
|
||||
}
|
||||
|
||||
|
||||
|
||||
internal TranslatedFunction GetOrTranslate(ulong address, ExecutionMode mode)
|
||||
{
|
||||
if (!Functions.TryGetValue(address, out TranslatedFunction func))
|
||||
@@ -230,7 +255,8 @@ namespace ARMeilleure.Translation
|
||||
address,
|
||||
highCq,
|
||||
_ptc.State != PtcState.Disabled,
|
||||
mode: Aarch32Mode.User);
|
||||
mode: Aarch32Mode.User,
|
||||
isSingleStep: singleStep);
|
||||
|
||||
Logger.StartPass(PassName.Decoding);
|
||||
|
||||
@@ -284,7 +310,7 @@ namespace ARMeilleure.Translation
|
||||
_ptc.WriteCompiledFunction(address, funcSize, hash, highCq, compiledFunc);
|
||||
}
|
||||
|
||||
GuestFunction func = compiledFunc.MapWithPointer<GuestFunction>(out nint funcPointer);
|
||||
GuestFunction func = compiledFunc.MapWithPointer<GuestFunction>(JitCache, out nint funcPointer);
|
||||
|
||||
Allocators.ResetAll();
|
||||
|
||||
@@ -369,9 +395,13 @@ namespace ARMeilleure.Translation
|
||||
|
||||
if (block.Exit)
|
||||
{
|
||||
// Left option here as it may be useful if we need to return to managed rather than tail call in
|
||||
// future. (eg. for debug)
|
||||
bool useReturns = false;
|
||||
// Return to managed rather than tail call.
|
||||
bool useReturns = Optimizations.EnableDebugging;
|
||||
|
||||
if (Optimizations.EnableDebugging)
|
||||
{
|
||||
EmitDebugPrecisePcUpdate(context, block.Address);
|
||||
}
|
||||
|
||||
InstEmitFlowHelper.EmitVirtualJump(context, Const(block.Address), isReturn: useReturns);
|
||||
}
|
||||
@@ -395,6 +425,11 @@ namespace ARMeilleure.Translation
|
||||
}
|
||||
}
|
||||
|
||||
if (Optimizations.EnableDebugging)
|
||||
{
|
||||
EmitDebugPrecisePcUpdate(context, opCode.Address);
|
||||
}
|
||||
|
||||
Operand lblPredicateSkip = default;
|
||||
|
||||
if (context.IsInIfThenBlock && context.CurrentIfThenBlockCond != Condition.Al)
|
||||
@@ -491,6 +526,14 @@ namespace ARMeilleure.Translation
|
||||
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)
|
||||
{
|
||||
ulong[] overlapAddresses = [];
|
||||
|
||||
@@ -14,6 +14,8 @@ namespace ARMeilleure.Translation
|
||||
/// </summary>
|
||||
class TranslatorStubs : IDisposable
|
||||
{
|
||||
private readonly JitCache _jitCache;
|
||||
|
||||
private readonly Lazy<nint> _slowDispatchStub;
|
||||
|
||||
private bool _disposed;
|
||||
@@ -83,12 +85,15 @@ namespace ARMeilleure.Translation
|
||||
/// Initializes a new instance of the <see cref="TranslatorStubs"/> class with the specified
|
||||
/// <see cref="Translator"/> instance.
|
||||
/// </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>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="translator"/> is null</exception>
|
||||
public TranslatorStubs(IAddressTable<ulong> functionTable)
|
||||
public TranslatorStubs(JitCache jitCache, IAddressTable<ulong> functionTable)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(functionTable);
|
||||
|
||||
_jitCache = jitCache;
|
||||
|
||||
_functionTable = functionTable;
|
||||
_slowDispatchStub = new(GenerateSlowDispatchStub, isThreadSafe: true);
|
||||
_dispatchStub = new(GenerateDispatchStub, isThreadSafe: true);
|
||||
@@ -115,12 +120,12 @@ namespace ARMeilleure.Translation
|
||||
{
|
||||
if (_dispatchStub.IsValueCreated)
|
||||
{
|
||||
JitCache.Unmap(_dispatchStub.Value);
|
||||
_jitCache.Unmap(_dispatchStub.Value);
|
||||
}
|
||||
|
||||
if (_dispatchLoop.IsValueCreated)
|
||||
{
|
||||
JitCache.Unmap(Marshal.GetFunctionPointerForDelegate(_dispatchLoop.Value));
|
||||
_jitCache.Unmap(Marshal.GetFunctionPointerForDelegate(_dispatchLoop.Value));
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
@@ -188,7 +193,7 @@ namespace ARMeilleure.Translation
|
||||
OperandType retType = OperandType.I64;
|
||||
OperandType[] argTypes = new[] { OperandType.I64 };
|
||||
|
||||
GuestFunction func = Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<GuestFunction>();
|
||||
GuestFunction func = Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<GuestFunction>(_jitCache);
|
||||
|
||||
return Marshal.GetFunctionPointerForDelegate(func);
|
||||
}
|
||||
@@ -213,7 +218,7 @@ namespace ARMeilleure.Translation
|
||||
OperandType retType = OperandType.I64;
|
||||
OperandType[] argTypes = new[] { OperandType.I64 };
|
||||
|
||||
GuestFunction func = Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<GuestFunction>();
|
||||
GuestFunction func = Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<GuestFunction>(_jitCache);
|
||||
|
||||
return Marshal.GetFunctionPointerForDelegate(func);
|
||||
}
|
||||
@@ -290,7 +295,7 @@ namespace ARMeilleure.Translation
|
||||
OperandType retType = OperandType.None;
|
||||
OperandType[] argTypes = new[] { OperandType.I64, OperandType.I64 };
|
||||
|
||||
return Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<DispatcherFunction>();
|
||||
return Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<DispatcherFunction>(_jitCache);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -314,7 +319,7 @@ namespace ARMeilleure.Translation
|
||||
OperandType retType = OperandType.I64;
|
||||
OperandType[] argTypes = new[] { OperandType.I64, OperandType.I64 };
|
||||
|
||||
return Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<WrapperFunction>();
|
||||
return Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<WrapperFunction>(_jitCache);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using ARMeilleure.CodeGen.X86;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.State;
|
||||
using ARMeilleure.Translation.Cache;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||
@@ -59,7 +60,7 @@ namespace ARMeilleure.Translation
|
||||
return context.VectorExtract(OperandType.I32, vec, 0);
|
||||
}
|
||||
|
||||
public static FpFlagsPInvokeTest GenerateFpFlagsPInvokeTest()
|
||||
public static FpFlagsPInvokeTest GenerateFpFlagsPInvokeTest(JitCache jitCache)
|
||||
{
|
||||
EmitterContext context = new();
|
||||
|
||||
@@ -141,7 +142,7 @@ namespace ARMeilleure.Translation
|
||||
|
||||
OperandType[] argTypes = [OperandType.I64];
|
||||
|
||||
return Compiler.Compile(cfg, argTypes, OperandType.I32, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<FpFlagsPInvokeTest>();
|
||||
return Compiler.Compile(cfg, argTypes, OperandType.I32, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<FpFlagsPInvokeTest>(jitCache);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
.idea/
|
||||
*.iml
|
||||
.gradle
|
||||
local.properties
|
||||
.DS_Store
|
||||
build/
|
||||
captures
|
||||
.externalNativeBuild
|
||||
.cxx/
|
||||
|
||||
app/src/main/jniLibs/arm64-v8a/**
|
||||
!app/src/main/jniLibs/arm64-v8a/.gitkeep
|
||||
@@ -1,175 +0,0 @@
|
||||
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
@@ -1,37 +0,0 @@
|
||||
-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*();
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
<?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>
|
||||
@@ -1,80 +0,0 @@
|
||||
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}
|
||||
)
|
||||
@@ -1,254 +0,0 @@
|
||||
// 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;
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
//
|
||||
// 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 deleted from 8fae8ce254
Submodule src/KenjinxAndroid/app/src/main/cpp/libraries/openal deleted from c41d64c6a3
@@ -1,305 +0,0 @@
|
||||
// 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);
|
||||
};
|
||||
@@ -1,404 +0,0 @@
|
||||
// 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;
|
||||
|
||||
@@ -1,236 +0,0 @@
|
||||
// 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.
|
Before Width: | Height: | Size: 14 KiB |
@@ -1,7 +0,0 @@
|
||||
package org.kenjinx.android
|
||||
|
||||
enum class BackendThreading {
|
||||
Auto,
|
||||
Off,
|
||||
On
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
package org.kenjinx.android
|
||||
|
||||
import androidx.activity.ComponentActivity
|
||||
|
||||
abstract class BaseActivity : ComponentActivity() {
|
||||
companion object {
|
||||
val crashHandler = CrashHandler()
|
||||
}
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
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())
|
||||
}
|
||||
}
|
||||
@@ -1,436 +0,0 @@
|
||||
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()
|
||||
),
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,420 +0,0 @@
|
||||
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()
|
||||
),
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,413 +0,0 @@
|
||||
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()
|
||||
),
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,410 +0,0 @@
|
||||
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()
|
||||
),
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,414 +0,0 @@
|
||||
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()
|
||||
),
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,411 +0,0 @@
|
||||
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()
|
||||
),
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,510 +0,0 @@
|
||||
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")
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
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
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
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
@@ -1,20 +0,0 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -1,371 +0,0 @@
|
||||
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
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
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
|
||||
}
|
||||
@@ -1,734 +0,0 @@
|
||||
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) { }
|
||||
}
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user