mirror of
https://git.ryujinx.app/ryubing/ryujinx.git
synced 2026-04-11 00:32:55 +00:00
Compare commits
12 Commits
Canary-1.3
...
Canary-1.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ed89ffd3f8 | ||
|
|
844d7a9cfe | ||
|
|
cf6acba416 | ||
|
|
5a9d5ee664 | ||
|
|
bbad867319 | ||
|
|
a8ace3d23c | ||
|
|
13b69aedfe | ||
|
|
234f7ca298 | ||
|
|
2c9b193018 | ||
|
|
92b61f9d73 | ||
|
|
ab7aeee67b | ||
|
|
b991fe05d9 |
@@ -16,6 +16,7 @@
|
||||
<PackageVersion Include="Projektanker.Icons.Avalonia" Version="9.6.2" />
|
||||
<PackageVersion Include="Projektanker.Icons.Avalonia.FontAwesome" Version="9.6.2" />
|
||||
<PackageVersion Include="Projektanker.Icons.Avalonia.MaterialDesign" Version="9.6.2" />
|
||||
<PackageVersion Include="ppy.SDL3-CS" Version="2025.920.0" />
|
||||
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
|
||||
<PackageVersion Include="CommunityToolkit.Mvvm" Version="8.4.0" />
|
||||
<PackageVersion Include="Concentus" Version="2.2.2" />
|
||||
@@ -41,7 +42,6 @@
|
||||
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies.AllArch" Version="6.1.2-build3" />
|
||||
<PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" />
|
||||
<PackageVersion Include="Ryujinx.LibHac" Version="0.21.0-alpha.126" />
|
||||
<PackageVersion Include="Ryujinx.SDL2-CS" Version="2.30.0-build32" />
|
||||
<PackageVersion Include="Ryujinx.UpdateClient" Version="1.0.44" />
|
||||
<PackageVersion Include="Ryujinx.Systems.Update.Common" Version="1.0.44" />
|
||||
<PackageVersion Include="Gommon" Version="2.8.0.1" />
|
||||
|
||||
@@ -66,7 +66,7 @@ If you are planning to contribute or just want to learn more about this project
|
||||
- **Audio**
|
||||
|
||||
Audio output is entirely supported, audio input (microphone) isn't supported.
|
||||
We use C# wrappers for [OpenAL](https://openal-soft.org/), and [SDL2](https://www.libsdl.org/) & [libsoundio](http://libsound.io/) as fallbacks.
|
||||
We use C# wrappers for [OpenAL](https://openal-soft.org/), and [SDL3](https://www.libsdl.org/) & [libsoundio](http://libsound.io/) as fallbacks.
|
||||
|
||||
- **CPU**
|
||||
|
||||
|
||||
355
Ryujinx.sln
355
Ryujinx.sln
@@ -51,12 +51,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio.Backends.Soun
|
||||
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}"
|
||||
@@ -89,171 +83,488 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
||||
nuget.config = nuget.config
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{827E0CD3-B72D-47B6-A68D-7590B98EB39B}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.SDL3.Common", "src\Ryujinx.SDL3.Common\Ryujinx.SDL3.Common.csproj", "{F6F9826A-BC58-4D78-A700-F358A66B2B06}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.Input.SDL3", "src\Ryujinx.Input.SDL3\Ryujinx.Input.SDL3.csproj", "{D728444C-3D1F-4A0E-B4C9-5C9375D47EA3}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.Audio.Backends.SDL3", "src\Ryujinx.Audio.Backends.SDL3\Ryujinx.Audio.Backends.SDL3.csproj", "{988E6191-82E1-4E13-9DDB-CB9FA2FDAF29}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|Any CPU = Release|Any CPU
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
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}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{EBB55AEA-C7D7-4DEB-BF96-FA1789E225E9}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{EBB55AEA-C7D7-4DEB-BF96-FA1789E225E9}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{EBB55AEA-C7D7-4DEB-BF96-FA1789E225E9}.Debug|x86.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
|
||||
{EBB55AEA-C7D7-4DEB-BF96-FA1789E225E9}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{EBB55AEA-C7D7-4DEB-BF96-FA1789E225E9}.Release|x64.Build.0 = Release|Any CPU
|
||||
{EBB55AEA-C7D7-4DEB-BF96-FA1789E225E9}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{EBB55AEA-C7D7-4DEB-BF96-FA1789E225E9}.Release|x86.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}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{D8F72938-78EF-4E8C-BAFE-531C9C3C8F15}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{D8F72938-78EF-4E8C-BAFE-531C9C3C8F15}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{D8F72938-78EF-4E8C-BAFE-531C9C3C8F15}.Debug|x86.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
|
||||
{D8F72938-78EF-4E8C-BAFE-531C9C3C8F15}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{D8F72938-78EF-4E8C-BAFE-531C9C3C8F15}.Release|x64.Build.0 = Release|Any CPU
|
||||
{D8F72938-78EF-4E8C-BAFE-531C9C3C8F15}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{D8F72938-78EF-4E8C-BAFE-531C9C3C8F15}.Release|x86.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}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{CB92CFF9-1D62-4D4F-9E88-8130EF61E351}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{CB92CFF9-1D62-4D4F-9E88-8130EF61E351}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{CB92CFF9-1D62-4D4F-9E88-8130EF61E351}.Debug|x86.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
|
||||
{CB92CFF9-1D62-4D4F-9E88-8130EF61E351}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{CB92CFF9-1D62-4D4F-9E88-8130EF61E351}.Release|x64.Build.0 = Release|Any CPU
|
||||
{CB92CFF9-1D62-4D4F-9E88-8130EF61E351}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{CB92CFF9-1D62-4D4F-9E88-8130EF61E351}.Release|x86.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}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{3AB294D0-2230-468F-9EB3-BDFCAEAE99A5}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{3AB294D0-2230-468F-9EB3-BDFCAEAE99A5}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{3AB294D0-2230-468F-9EB3-BDFCAEAE99A5}.Debug|x86.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
|
||||
{3AB294D0-2230-468F-9EB3-BDFCAEAE99A5}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{3AB294D0-2230-468F-9EB3-BDFCAEAE99A5}.Release|x64.Build.0 = Release|Any CPU
|
||||
{3AB294D0-2230-468F-9EB3-BDFCAEAE99A5}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{3AB294D0-2230-468F-9EB3-BDFCAEAE99A5}.Release|x86.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}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{5FD4E4F6-8928-4B3C-BE07-28A675C17226}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{5FD4E4F6-8928-4B3C-BE07-28A675C17226}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{5FD4E4F6-8928-4B3C-BE07-28A675C17226}.Debug|x86.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
|
||||
{5FD4E4F6-8928-4B3C-BE07-28A675C17226}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{5FD4E4F6-8928-4B3C-BE07-28A675C17226}.Release|x64.Build.0 = Release|Any CPU
|
||||
{5FD4E4F6-8928-4B3C-BE07-28A675C17226}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{5FD4E4F6-8928-4B3C-BE07-28A675C17226}.Release|x86.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}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{ABF09A5E-2D8B-4B6F-A51D-5CE414DDB15A}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{ABF09A5E-2D8B-4B6F-A51D-5CE414DDB15A}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{ABF09A5E-2D8B-4B6F-A51D-5CE414DDB15A}.Debug|x86.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
|
||||
{ABF09A5E-2D8B-4B6F-A51D-5CE414DDB15A}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{ABF09A5E-2D8B-4B6F-A51D-5CE414DDB15A}.Release|x64.Build.0 = Release|Any CPU
|
||||
{ABF09A5E-2D8B-4B6F-A51D-5CE414DDB15A}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{ABF09A5E-2D8B-4B6F-A51D-5CE414DDB15A}.Release|x86.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}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{ADA7EA87-0D63-4D97-9433-922A2124401F}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{ADA7EA87-0D63-4D97-9433-922A2124401F}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{ADA7EA87-0D63-4D97-9433-922A2124401F}.Debug|x86.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
|
||||
{ADA7EA87-0D63-4D97-9433-922A2124401F}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{ADA7EA87-0D63-4D97-9433-922A2124401F}.Release|x64.Build.0 = Release|Any CPU
|
||||
{ADA7EA87-0D63-4D97-9433-922A2124401F}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{ADA7EA87-0D63-4D97-9433-922A2124401F}.Release|x86.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}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{A602AE97-91A5-4608-8DF1-EBF4ED7A0B9E}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{A602AE97-91A5-4608-8DF1-EBF4ED7A0B9E}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{A602AE97-91A5-4608-8DF1-EBF4ED7A0B9E}.Debug|x86.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
|
||||
{A602AE97-91A5-4608-8DF1-EBF4ED7A0B9E}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{A602AE97-91A5-4608-8DF1-EBF4ED7A0B9E}.Release|x64.Build.0 = Release|Any CPU
|
||||
{A602AE97-91A5-4608-8DF1-EBF4ED7A0B9E}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{A602AE97-91A5-4608-8DF1-EBF4ED7A0B9E}.Release|x86.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}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{9558FB96-075D-4219-8FFF-401979DC0B69}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{9558FB96-075D-4219-8FFF-401979DC0B69}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{9558FB96-075D-4219-8FFF-401979DC0B69}.Debug|x86.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
|
||||
{9558FB96-075D-4219-8FFF-401979DC0B69}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{9558FB96-075D-4219-8FFF-401979DC0B69}.Release|x64.Build.0 = Release|Any CPU
|
||||
{9558FB96-075D-4219-8FFF-401979DC0B69}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{9558FB96-075D-4219-8FFF-401979DC0B69}.Release|x86.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}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{E1B1AD28-289D-47B7-A106-326972240207}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{E1B1AD28-289D-47B7-A106-326972240207}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{E1B1AD28-289D-47B7-A106-326972240207}.Debug|x86.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
|
||||
{E1B1AD28-289D-47B7-A106-326972240207}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{E1B1AD28-289D-47B7-A106-326972240207}.Release|x64.Build.0 = Release|Any CPU
|
||||
{E1B1AD28-289D-47B7-A106-326972240207}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{E1B1AD28-289D-47B7-A106-326972240207}.Release|x86.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}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{03B955CD-AD84-4B93-AAA7-BF17923BBAA5}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{03B955CD-AD84-4B93-AAA7-BF17923BBAA5}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{03B955CD-AD84-4B93-AAA7-BF17923BBAA5}.Debug|x86.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
|
||||
{03B955CD-AD84-4B93-AAA7-BF17923BBAA5}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{03B955CD-AD84-4B93-AAA7-BF17923BBAA5}.Release|x64.Build.0 = Release|Any CPU
|
||||
{03B955CD-AD84-4B93-AAA7-BF17923BBAA5}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{03B955CD-AD84-4B93-AAA7-BF17923BBAA5}.Release|x86.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}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{85A0FA56-DC01-4A42-8808-70DAC76BD66D}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{85A0FA56-DC01-4A42-8808-70DAC76BD66D}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{85A0FA56-DC01-4A42-8808-70DAC76BD66D}.Debug|x86.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
|
||||
{85A0FA56-DC01-4A42-8808-70DAC76BD66D}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{85A0FA56-DC01-4A42-8808-70DAC76BD66D}.Release|x64.Build.0 = Release|Any CPU
|
||||
{85A0FA56-DC01-4A42-8808-70DAC76BD66D}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{85A0FA56-DC01-4A42-8808-70DAC76BD66D}.Release|x86.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}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{806ACF6D-90B0-45D0-A1AC-5F220F3B3985}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{806ACF6D-90B0-45D0-A1AC-5F220F3B3985}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{806ACF6D-90B0-45D0-A1AC-5F220F3B3985}.Debug|x86.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
|
||||
{806ACF6D-90B0-45D0-A1AC-5F220F3B3985}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{806ACF6D-90B0-45D0-A1AC-5F220F3B3985}.Release|x64.Build.0 = Release|Any CPU
|
||||
{806ACF6D-90B0-45D0-A1AC-5F220F3B3985}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{806ACF6D-90B0-45D0-A1AC-5F220F3B3985}.Release|x86.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}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{A5E6C691-9E22-4263-8F40-42F002CE66BE}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{A5E6C691-9E22-4263-8F40-42F002CE66BE}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{A5E6C691-9E22-4263-8F40-42F002CE66BE}.Debug|x86.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
|
||||
{A5E6C691-9E22-4263-8F40-42F002CE66BE}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{A5E6C691-9E22-4263-8F40-42F002CE66BE}.Release|x64.Build.0 = Release|Any CPU
|
||||
{A5E6C691-9E22-4263-8F40-42F002CE66BE}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{A5E6C691-9E22-4263-8F40-42F002CE66BE}.Release|x86.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}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{D1CC5322-7325-4F6B-9625-194B30BE1296}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{D1CC5322-7325-4F6B-9625-194B30BE1296}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{D1CC5322-7325-4F6B-9625-194B30BE1296}.Debug|x86.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
|
||||
{D1CC5322-7325-4F6B-9625-194B30BE1296}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{D1CC5322-7325-4F6B-9625-194B30BE1296}.Release|x64.Build.0 = Release|Any CPU
|
||||
{D1CC5322-7325-4F6B-9625-194B30BE1296}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{D1CC5322-7325-4F6B-9625-194B30BE1296}.Release|x86.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}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{3DF35E3D-D844-4399-A9A1-A9E923264C17}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{3DF35E3D-D844-4399-A9A1-A9E923264C17}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{3DF35E3D-D844-4399-A9A1-A9E923264C17}.Debug|x86.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
|
||||
{3DF35E3D-D844-4399-A9A1-A9E923264C17}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{3DF35E3D-D844-4399-A9A1-A9E923264C17}.Release|x64.Build.0 = Release|Any CPU
|
||||
{3DF35E3D-D844-4399-A9A1-A9E923264C17}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{3DF35E3D-D844-4399-A9A1-A9E923264C17}.Release|x86.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}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{C3002C3C-7B09-4FE7-894A-372EDA22FC6E}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{C3002C3C-7B09-4FE7-894A-372EDA22FC6E}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{C3002C3C-7B09-4FE7-894A-372EDA22FC6E}.Debug|x86.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
|
||||
{C3002C3C-7B09-4FE7-894A-372EDA22FC6E}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{C3002C3C-7B09-4FE7-894A-372EDA22FC6E}.Release|x64.Build.0 = Release|Any CPU
|
||||
{C3002C3C-7B09-4FE7-894A-372EDA22FC6E}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{C3002C3C-7B09-4FE7-894A-372EDA22FC6E}.Release|x86.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}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{C35F1536-7DE5-4F9D-9604-B5B4E1561947}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{C35F1536-7DE5-4F9D-9604-B5B4E1561947}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{C35F1536-7DE5-4F9D-9604-B5B4E1561947}.Debug|x86.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
|
||||
{C35F1536-7DE5-4F9D-9604-B5B4E1561947}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{C35F1536-7DE5-4F9D-9604-B5B4E1561947}.Release|x64.Build.0 = Release|Any CPU
|
||||
{C35F1536-7DE5-4F9D-9604-B5B4E1561947}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{C35F1536-7DE5-4F9D-9604-B5B4E1561947}.Release|x86.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}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{B9AECA11-E248-4886-A10B-81B631CAAF29}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{B9AECA11-E248-4886-A10B-81B631CAAF29}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{B9AECA11-E248-4886-A10B-81B631CAAF29}.Debug|x86.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
|
||||
{B9AECA11-E248-4886-A10B-81B631CAAF29}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{B9AECA11-E248-4886-A10B-81B631CAAF29}.Release|x64.Build.0 = Release|Any CPU
|
||||
{B9AECA11-E248-4886-A10B-81B631CAAF29}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{B9AECA11-E248-4886-A10B-81B631CAAF29}.Release|x86.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}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{81BB2C11-9408-4EA3-822E-42987AF54429}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{81BB2C11-9408-4EA3-822E-42987AF54429}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{81BB2C11-9408-4EA3-822E-42987AF54429}.Debug|x86.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
|
||||
{81BB2C11-9408-4EA3-822E-42987AF54429}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{81BB2C11-9408-4EA3-822E-42987AF54429}.Release|x64.Build.0 = Release|Any CPU
|
||||
{81BB2C11-9408-4EA3-822E-42987AF54429}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{81BB2C11-9408-4EA3-822E-42987AF54429}.Release|x86.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}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{FD4A2C14-8E3D-4957-ABBE-3C38897B3E2D}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{FD4A2C14-8E3D-4957-ABBE-3C38897B3E2D}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{FD4A2C14-8E3D-4957-ABBE-3C38897B3E2D}.Debug|x86.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
|
||||
{FD4A2C14-8E3D-4957-ABBE-3C38897B3E2D}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{FD4A2C14-8E3D-4957-ABBE-3C38897B3E2D}.Release|x64.Build.0 = Release|Any CPU
|
||||
{FD4A2C14-8E3D-4957-ABBE-3C38897B3E2D}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{FD4A2C14-8E3D-4957-ABBE-3C38897B3E2D}.Release|x86.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}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{0BE11899-DF2D-4BDE-B9EE-2489E8D35E7D}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{0BE11899-DF2D-4BDE-B9EE-2489E8D35E7D}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{0BE11899-DF2D-4BDE-B9EE-2489E8D35E7D}.Debug|x86.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
|
||||
{0BE11899-DF2D-4BDE-B9EE-2489E8D35E7D}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{0BE11899-DF2D-4BDE-B9EE-2489E8D35E7D}.Release|x64.Build.0 = Release|Any CPU
|
||||
{0BE11899-DF2D-4BDE-B9EE-2489E8D35E7D}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{0BE11899-DF2D-4BDE-B9EE-2489E8D35E7D}.Release|x86.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}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{716364DE-B988-41A6-BAB4-327964266ECC}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{716364DE-B988-41A6-BAB4-327964266ECC}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{716364DE-B988-41A6-BAB4-327964266ECC}.Debug|x86.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
|
||||
{716364DE-B988-41A6-BAB4-327964266ECC}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{716364DE-B988-41A6-BAB4-327964266ECC}.Release|x64.Build.0 = Release|Any CPU
|
||||
{716364DE-B988-41A6-BAB4-327964266ECC}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{716364DE-B988-41A6-BAB4-327964266ECC}.Release|x86.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}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{C16F112F-38C3-40BC-9F5F-4791112063D6}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{C16F112F-38C3-40BC-9F5F-4791112063D6}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{C16F112F-38C3-40BC-9F5F-4791112063D6}.Debug|x86.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
|
||||
{C16F112F-38C3-40BC-9F5F-4791112063D6}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{C16F112F-38C3-40BC-9F5F-4791112063D6}.Release|x64.Build.0 = Release|Any CPU
|
||||
{C16F112F-38C3-40BC-9F5F-4791112063D6}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{C16F112F-38C3-40BC-9F5F-4791112063D6}.Release|x86.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}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{BEE1C184-C9A4-410B-8DFC-FB74D5C93AEB}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{BEE1C184-C9A4-410B-8DFC-FB74D5C93AEB}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{BEE1C184-C9A4-410B-8DFC-FB74D5C93AEB}.Debug|x86.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
|
||||
{BEE1C184-C9A4-410B-8DFC-FB74D5C93AEB}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{BEE1C184-C9A4-410B-8DFC-FB74D5C93AEB}.Release|x64.Build.0 = Release|Any CPU
|
||||
{BEE1C184-C9A4-410B-8DFC-FB74D5C93AEB}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{BEE1C184-C9A4-410B-8DFC-FB74D5C93AEB}.Release|x86.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}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{7C1B2721-13DA-4B62-B046-C626605ECCE6}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{7C1B2721-13DA-4B62-B046-C626605ECCE6}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{7C1B2721-13DA-4B62-B046-C626605ECCE6}.Debug|x86.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
|
||||
{7C1B2721-13DA-4B62-B046-C626605ECCE6}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{7C1B2721-13DA-4B62-B046-C626605ECCE6}.Release|x64.Build.0 = Release|Any CPU
|
||||
{7C1B2721-13DA-4B62-B046-C626605ECCE6}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{7C1B2721-13DA-4B62-B046-C626605ECCE6}.Release|x86.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}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{6AE2A5E8-4C5A-48B9-997B-E1455C0355C6}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{6AE2A5E8-4C5A-48B9-997B-E1455C0355C6}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{6AE2A5E8-4C5A-48B9-997B-E1455C0355C6}.Debug|x86.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
|
||||
{6AE2A5E8-4C5A-48B9-997B-E1455C0355C6}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{6AE2A5E8-4C5A-48B9-997B-E1455C0355C6}.Release|x64.Build.0 = Release|Any CPU
|
||||
{6AE2A5E8-4C5A-48B9-997B-E1455C0355C6}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{6AE2A5E8-4C5A-48B9-997B-E1455C0355C6}.Release|x86.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}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{D4D09B08-D580-4D69-B886-C35D2853F6C8}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{D4D09B08-D580-4D69-B886-C35D2853F6C8}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{D4D09B08-D580-4D69-B886-C35D2853F6C8}.Debug|x86.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
|
||||
{D4D09B08-D580-4D69-B886-C35D2853F6C8}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{D4D09B08-D580-4D69-B886-C35D2853F6C8}.Release|x64.Build.0 = Release|Any CPU
|
||||
{D4D09B08-D580-4D69-B886-C35D2853F6C8}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{D4D09B08-D580-4D69-B886-C35D2853F6C8}.Release|x86.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}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{2BCB3D7A-38C0-4FE7-8FDA-374C6AD56D0E}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{2BCB3D7A-38C0-4FE7-8FDA-374C6AD56D0E}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{2BCB3D7A-38C0-4FE7-8FDA-374C6AD56D0E}.Debug|x86.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
|
||||
{2BCB3D7A-38C0-4FE7-8FDA-374C6AD56D0E}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{2BCB3D7A-38C0-4FE7-8FDA-374C6AD56D0E}.Release|x64.Build.0 = Release|Any CPU
|
||||
{2BCB3D7A-38C0-4FE7-8FDA-374C6AD56D0E}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{2BCB3D7A-38C0-4FE7-8FDA-374C6AD56D0E}.Release|x86.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}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{77D01AD9-2C98-478E-AE1D-8F7100738FB4}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{77D01AD9-2C98-478E-AE1D-8F7100738FB4}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{77D01AD9-2C98-478E-AE1D-8F7100738FB4}.Debug|x86.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
|
||||
{77D01AD9-2C98-478E-AE1D-8F7100738FB4}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{77D01AD9-2C98-478E-AE1D-8F7100738FB4}.Release|x64.Build.0 = Release|Any CPU
|
||||
{77D01AD9-2C98-478E-AE1D-8F7100738FB4}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{77D01AD9-2C98-478E-AE1D-8F7100738FB4}.Release|x86.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}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{77F96ECE-4952-42DB-A528-DED25572A573}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{77F96ECE-4952-42DB-A528-DED25572A573}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{77F96ECE-4952-42DB-A528-DED25572A573}.Debug|x86.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
|
||||
{77F96ECE-4952-42DB-A528-DED25572A573}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{77F96ECE-4952-42DB-A528-DED25572A573}.Release|x64.Build.0 = Release|Any CPU
|
||||
{77F96ECE-4952-42DB-A528-DED25572A573}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{77F96ECE-4952-42DB-A528-DED25572A573}.Release|x86.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}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{AF34127A-3A92-43E5-8496-14960A50B1F1}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{AF34127A-3A92-43E5-8496-14960A50B1F1}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{AF34127A-3A92-43E5-8496-14960A50B1F1}.Debug|x86.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
|
||||
{AF34127A-3A92-43E5-8496-14960A50B1F1}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{AF34127A-3A92-43E5-8496-14960A50B1F1}.Release|x64.Build.0 = Release|Any CPU
|
||||
{AF34127A-3A92-43E5-8496-14960A50B1F1}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{AF34127A-3A92-43E5-8496-14960A50B1F1}.Release|x86.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}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}.Debug|x86.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
|
||||
{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}.Release|x64.Build.0 = Release|Any CPU
|
||||
{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}.Release|x86.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}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Debug|x86.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
|
||||
{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Release|x64.Build.0 = Release|Any CPU
|
||||
{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Release|x86.Build.0 = Release|Any CPU
|
||||
{4A89A234-4F19-497D-A576-DDE8CDFC5B22}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4A89A234-4F19-497D-A576-DDE8CDFC5B22}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4A89A234-4F19-497D-A576-DDE8CDFC5B22}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{4A89A234-4F19-497D-A576-DDE8CDFC5B22}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{4A89A234-4F19-497D-A576-DDE8CDFC5B22}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{4A89A234-4F19-497D-A576-DDE8CDFC5B22}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{4A89A234-4F19-497D-A576-DDE8CDFC5B22}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4A89A234-4F19-497D-A576-DDE8CDFC5B22}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{4A89A234-4F19-497D-A576-DDE8CDFC5B22}.Release|x64.Build.0 = Release|Any CPU
|
||||
{4A89A234-4F19-497D-A576-DDE8CDFC5B22}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{4A89A234-4F19-497D-A576-DDE8CDFC5B22}.Release|x86.Build.0 = Release|Any CPU
|
||||
{F6F9826A-BC58-4D78-A700-F358A66B2B06}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F6F9826A-BC58-4D78-A700-F358A66B2B06}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F6F9826A-BC58-4D78-A700-F358A66B2B06}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{F6F9826A-BC58-4D78-A700-F358A66B2B06}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{F6F9826A-BC58-4D78-A700-F358A66B2B06}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{F6F9826A-BC58-4D78-A700-F358A66B2B06}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{F6F9826A-BC58-4D78-A700-F358A66B2B06}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F6F9826A-BC58-4D78-A700-F358A66B2B06}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{F6F9826A-BC58-4D78-A700-F358A66B2B06}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{F6F9826A-BC58-4D78-A700-F358A66B2B06}.Release|x64.Build.0 = Release|Any CPU
|
||||
{F6F9826A-BC58-4D78-A700-F358A66B2B06}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{F6F9826A-BC58-4D78-A700-F358A66B2B06}.Release|x86.Build.0 = Release|Any CPU
|
||||
{D728444C-3D1F-4A0E-B4C9-5C9375D47EA3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D728444C-3D1F-4A0E-B4C9-5C9375D47EA3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D728444C-3D1F-4A0E-B4C9-5C9375D47EA3}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{D728444C-3D1F-4A0E-B4C9-5C9375D47EA3}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{D728444C-3D1F-4A0E-B4C9-5C9375D47EA3}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{D728444C-3D1F-4A0E-B4C9-5C9375D47EA3}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{D728444C-3D1F-4A0E-B4C9-5C9375D47EA3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D728444C-3D1F-4A0E-B4C9-5C9375D47EA3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D728444C-3D1F-4A0E-B4C9-5C9375D47EA3}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{D728444C-3D1F-4A0E-B4C9-5C9375D47EA3}.Release|x64.Build.0 = Release|Any CPU
|
||||
{D728444C-3D1F-4A0E-B4C9-5C9375D47EA3}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{D728444C-3D1F-4A0E-B4C9-5C9375D47EA3}.Release|x86.Build.0 = Release|Any CPU
|
||||
{988E6191-82E1-4E13-9DDB-CB9FA2FDAF29}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{988E6191-82E1-4E13-9DDB-CB9FA2FDAF29}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{988E6191-82E1-4E13-9DDB-CB9FA2FDAF29}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{988E6191-82E1-4E13-9DDB-CB9FA2FDAF29}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{988E6191-82E1-4E13-9DDB-CB9FA2FDAF29}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{988E6191-82E1-4E13-9DDB-CB9FA2FDAF29}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{988E6191-82E1-4E13-9DDB-CB9FA2FDAF29}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{988E6191-82E1-4E13-9DDB-CB9FA2FDAF29}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{988E6191-82E1-4E13-9DDB-CB9FA2FDAF29}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{988E6191-82E1-4E13-9DDB-CB9FA2FDAF29}.Release|x64.Build.0 = Release|Any CPU
|
||||
{988E6191-82E1-4E13-9DDB-CB9FA2FDAF29}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{988E6191-82E1-4E13-9DDB-CB9FA2FDAF29}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{F6F9826A-BC58-4D78-A700-F358A66B2B06} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
|
||||
{D728444C-3D1F-4A0E-B4C9-5C9375D47EA3} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
|
||||
{988E6191-82E1-4E13-9DDB-CB9FA2FDAF29} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {110169B3-3328-4730-8AB0-BA05BEF75C1A}
|
||||
EndGlobalSection
|
||||
|
||||
@@ -89,7 +89,7 @@
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Редактор Mii",
|
||||
"zh_CN": "Mii 编辑器",
|
||||
"zh_TW": ""
|
||||
"zh_TW": "Mii 編輯器"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -295,7 +295,7 @@
|
||||
{
|
||||
"ID": "MenuBarFileOpenFromFile",
|
||||
"Translations": {
|
||||
"ar_SA": "_تحميل التطبيق...",
|
||||
"ar_SA": "_تحميل التطبيق...",
|
||||
"de_DE": "_Anwendung laden...",
|
||||
"el_GR": "_Φόρτωση εφαρμογής...",
|
||||
"en_US": "_Load Application...",
|
||||
@@ -1064,7 +1064,7 @@
|
||||
"tr_TR": "",
|
||||
"uk_UA": "",
|
||||
"zh_CN": "工具",
|
||||
"zh_TW": ""
|
||||
"zh_TW": "工具"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -1314,7 +1314,7 @@
|
||||
"tr_TR": "",
|
||||
"uk_UA": "FAQ & Усунення несправностей",
|
||||
"zh_CN": "常见问题与疑难解答",
|
||||
"zh_TW": ""
|
||||
"zh_TW": "常見問題與疑難排解"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -1346,7 +1346,7 @@
|
||||
"ID": "MenuBarHelpMultiplayer",
|
||||
"Translations": {
|
||||
"ar_SA": "متعدد اللاعبين (LDN/LAN)",
|
||||
"de_DE": "Multiplayer (LDN/LAN)",
|
||||
"de_DE": "",
|
||||
"el_GR": "Πολλαπλοί Παίκτες (LDN/LAN)",
|
||||
"en_US": "Multiplayer (LDN/LAN)",
|
||||
"es_ES": "Multijugador (LDN/LAN)",
|
||||
@@ -2527,7 +2527,7 @@
|
||||
"es_ES": "Logotipo",
|
||||
"fr_FR": null,
|
||||
"he_IL": "",
|
||||
"it_IT": "Logo",
|
||||
"it_IT": "",
|
||||
"ja_JP": "ロゴ",
|
||||
"ko_KR": "로고",
|
||||
"no_NO": "",
|
||||
@@ -3250,14 +3250,14 @@
|
||||
"el_GR": "Διεπαφή",
|
||||
"en_US": "Interface",
|
||||
"es_ES": "Interfaz",
|
||||
"fr_FR": "Interface",
|
||||
"fr_FR": "",
|
||||
"he_IL": "ממשק",
|
||||
"it_IT": "Interfaccia",
|
||||
"ja_JP": "インターフェース",
|
||||
"ko_KR": "인터페이스",
|
||||
"no_NO": "Grensesnitt",
|
||||
"pl_PL": "Interfejs",
|
||||
"pt_BR": "Interface",
|
||||
"pt_BR": "",
|
||||
"ru_RU": "Интерфейс",
|
||||
"sv_SE": "Gränssnitt",
|
||||
"th_TH": "อินเทอร์เฟซ",
|
||||
@@ -4027,7 +4027,7 @@
|
||||
"es_ES": null,
|
||||
"fr_FR": "Australie",
|
||||
"he_IL": "אוסטרליה",
|
||||
"it_IT": "Australia",
|
||||
"it_IT": "",
|
||||
"ja_JP": "オーストラリア",
|
||||
"ko_KR": "호주",
|
||||
"no_NO": "",
|
||||
@@ -4893,12 +4893,12 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "SettingsTabSystemAudioBackendSDL2",
|
||||
"ID": "SettingsTabSystemAudioBackendSDL3",
|
||||
"Translations": {
|
||||
"ar_SA": null,
|
||||
"de_DE": null,
|
||||
"el_GR": null,
|
||||
"en_US": "SDL2",
|
||||
"en_US": "SDL3",
|
||||
"es_ES": null,
|
||||
"fr_FR": null,
|
||||
"he_IL": null,
|
||||
@@ -6465,7 +6465,6 @@
|
||||
"uk_UA": "Я хочу скинути налаштування.",
|
||||
"zh_CN": "我要重置我的设置。",
|
||||
"zh_TW": "我想重設我的設定。"
|
||||
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -14655,7 +14654,7 @@
|
||||
"he_IL": "",
|
||||
"it_IT": "",
|
||||
"ja_JP": "",
|
||||
"ko_KR": "",
|
||||
"ko_KR": "{0} DRAM 활성화",
|
||||
"no_NO": "",
|
||||
"pl_PL": "",
|
||||
"pt_BR": "",
|
||||
@@ -14665,7 +14664,7 @@
|
||||
"tr_TR": "",
|
||||
"uk_UA": "",
|
||||
"zh_CN": "",
|
||||
"zh_TW": ""
|
||||
"zh_TW": "將使用 {0} DRAM"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -14680,7 +14679,7 @@
|
||||
"he_IL": "",
|
||||
"it_IT": "",
|
||||
"ja_JP": "",
|
||||
"ko_KR": "",
|
||||
"ko_KR": "4GB 이상의 DRAM을 사용하면 일부 앱에서 충돌이 발생할 수 있습니다.",
|
||||
"no_NO": "",
|
||||
"pl_PL": "",
|
||||
"pt_BR": "",
|
||||
@@ -14690,7 +14689,7 @@
|
||||
"tr_TR": "",
|
||||
"uk_UA": "",
|
||||
"zh_CN": "",
|
||||
"zh_TW": ""
|
||||
"zh_TW": "使用超過 4GiB DRAM 有機會於模擬某些應用程式時停止運作。"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -14705,7 +14704,7 @@
|
||||
"he_IL": "",
|
||||
"it_IT": "",
|
||||
"ja_JP": "",
|
||||
"ko_KR": "",
|
||||
"ko_KR": "디버그 : GDB Stub 활성화됨(포트 : {0})",
|
||||
"no_NO": "",
|
||||
"pl_PL": "",
|
||||
"pt_BR": "",
|
||||
@@ -14715,7 +14714,7 @@
|
||||
"tr_TR": "",
|
||||
"uk_UA": "",
|
||||
"zh_CN": "",
|
||||
"zh_TW": ""
|
||||
"zh_TW": "偵錯:已啟用 GDB Stub (通訊埠:{0})"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -14730,7 +14729,7 @@
|
||||
"he_IL": "",
|
||||
"it_IT": "",
|
||||
"ja_JP": "",
|
||||
"ko_KR": "",
|
||||
"ko_KR": "이는 성능에 영향을 미칠 것입니다.",
|
||||
"no_NO": "",
|
||||
"pl_PL": "",
|
||||
"pt_BR": "",
|
||||
@@ -14740,7 +14739,7 @@
|
||||
"tr_TR": "",
|
||||
"uk_UA": "",
|
||||
"zh_CN": "",
|
||||
"zh_TW": ""
|
||||
"zh_TW": "啟用此選項會影響效能。"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -14755,7 +14754,7 @@
|
||||
"he_IL": "",
|
||||
"it_IT": "",
|
||||
"ja_JP": "",
|
||||
"ko_KR": "",
|
||||
"ko_KR": "디버그 : 시작 시, 일시 중지 활성화",
|
||||
"no_NO": "",
|
||||
"pl_PL": "",
|
||||
"pt_BR": "",
|
||||
@@ -14765,7 +14764,7 @@
|
||||
"tr_TR": "",
|
||||
"uk_UA": "",
|
||||
"zh_CN": "",
|
||||
"zh_TW": ""
|
||||
"zh_TW": "偵錯:已啟用執行時暫停應用程式"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -14780,7 +14779,7 @@
|
||||
"he_IL": "",
|
||||
"it_IT": "",
|
||||
"ja_JP": "",
|
||||
"ko_KR": "",
|
||||
"ko_KR": "앱이 일시 중지되었습니다. 계속하려면 디버거를 연결하세요.",
|
||||
"no_NO": "",
|
||||
"pl_PL": "",
|
||||
"pt_BR": "",
|
||||
@@ -14790,7 +14789,7 @@
|
||||
"tr_TR": "",
|
||||
"uk_UA": "",
|
||||
"zh_CN": "",
|
||||
"zh_TW": ""
|
||||
"zh_TW": "應用程式已暫停。附加偵錯器以繼續。"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -16371,26 +16370,26 @@
|
||||
{
|
||||
"ID": "AudioBackendTooltip",
|
||||
"Translations": {
|
||||
"ar_SA": "يغير الواجهة الخلفية المستخدمة لتقديم الصوت.\n\nSDL2 هو الخيار المفضل، بينما يتم استخدام OpenAL وSoundIO كبديلين. زائف لن يكون لها صوت.\n\nاضبط على SDL2 إذا لم تكن متأكدا.",
|
||||
"de_DE": "Ändert das Backend, das zum Rendern von Audio verwendet wird.\n\nSDL2 ist das bevorzugte Audio-Backend, OpenAL und SoundIO sind als Alternativen vorhanden. Dummy wird keinen Audio-Output haben.\n\nIm Zweifelsfall SDL2 auswählen.",
|
||||
"ar_SA": "يغير الواجهة الخلفية المستخدمة لتقديم الصوت.\n\nSDL3 هو الخيار المفضل، بينما يتم استخدام OpenAL وSoundIO كبديلين. زائف لن يكون لها صوت.\n\nاضبط على SDL3 إذا لم تكن متأكدا.",
|
||||
"de_DE": "Ändert das Backend, das zum Rendern von Audio verwendet wird.\n\nSDL3 ist das bevorzugte Audio-Backend, OpenAL und SoundIO sind als Alternativen vorhanden. Dummy wird keinen Audio-Output haben.\n\nIm Zweifelsfall SDL3 auswählen.",
|
||||
"el_GR": "Αλλαγή ήχου υποστήριξης",
|
||||
"en_US": "Changes the backend used to render audio.\n\nSDL2 is the preferred one, while OpenAL and SoundIO are used as fallbacks. Dummy will have no sound.\n\nSet to SDL2 if unsure.",
|
||||
"es_ES": "Cambia el motor usado para renderizar audio.\n\nSDL2 es el preferido, mientras que OpenAL y SoundIO se usan si hay problemas con este. Dummy no produce audio.\n\nSelecciona SDL2 si no sabes qué hacer.",
|
||||
"fr_FR": "Modifie la backend utilisé pour donner un rendu audio.\n\nSDL2 est recommandé, tandis que OpenAL et SoundIO sont utilisés en secours. Dummy ne produit aucun son.\n\nLaissez sur SDL2 si vous n'êtes pas sûr.",
|
||||
"he_IL": "משנה את אחראי השמע.\n\nSDL2 הוא הנבחר, למראת שOpenAL וגם SoundIO משומשים כאפשרויות חלופיות. אפשרות הDummy לא תשמיע קול כלל.\n\nמוטב להשאיר על SDL2 אם לא בטוחים.",
|
||||
"it_IT": "Cambia il backend usato per riprodurre l'audio.\n\nSDL2 è quello preferito, mentre OpenAL e SoundIO sono usati come ripiego. Dummy non riprodurrà alcun suono.\n\nNel dubbio, imposta l'opzione su SDL2.",
|
||||
"ja_JP": "音声レンダリングに使用するバックエンドを変更します.\n\nSDL2 が優先され, OpenAL と SoundIO はフォールバックとして使用されます. ダミーは音声出力しません.\n\nよくわからない場合は SDL2 を設定してください.",
|
||||
"ko_KR": "오디오 렌더링에 사용되는 백엔드를 변경합니다.\n\nSDL2가 선호되는 반면 OpenAL 및 SoundIO는 대체 수단으로 사용됩니다. 더미에는 소리가 나지 않습니다.\n\n모르면 SDL2로 설정하세요.",
|
||||
"no_NO": "Endrer backend brukt til å gjengi lyd.\n\nSDL2 er foretrukket, mens OpenAL og SoundIO brukes som reserveløsning. Dummy kommer ikke til å ha lyd.\n\nSett til SDL2 hvis usikker.",
|
||||
"pl_PL": "Zmienia backend używany do renderowania dźwięku.\n\nSDL2 jest preferowany, podczas gdy OpenAL i SoundIO są używane jako rezerwy. Dummy nie będzie odtwarzać dźwięku.\n\nW razie wątpliwości ustaw SDL2.",
|
||||
"pt_BR": "Altera o módulo usado para renderizar áudio.\n\nSDL2 é o preferido, enquanto OpenAL e SoundIO são usados como fallbacks. Dummy não terá som.\n\nDefina como SDL2 se não tiver certeza.",
|
||||
"ru_RU": "Меняет бэкенд используемый для воспроизведения аудио.\n\nSDL2 — предпочтительный вариант, в то время как OpenAL и SoundIO используются как резервные. Dummy не будет воспроизводить звук.\n\nРекомендуется использовать SDL2.",
|
||||
"sv_SE": "Ändrar bakänden som används för att rendera ljud.\n\nSDL2 är den föredragna, men OpenAL och SoundIO används för att falla tillbaka på. Dummy har inget ljud.\n\nStäll in till SDL2 om du är osäker.",
|
||||
"th_TH": "เปลี่ยนแบ็กเอนด์ที่ใช้ในการเรนเดอร์เสียง\n\nแนะนำเป็น SDL2 ในขณะที่ OpenAL และ SoundIO ถูกใช้เป็นทางเลือกสำรอง ดัมมี่จะไม่มีเสียง\n\nตั้งค่าเป็น SDL2 หากคุณไม่แน่ใจ",
|
||||
"tr_TR": "Ses çıkış motorunu değiştirir.\n\nSDL2 tercih edilen seçenektir, OpenAL ve SoundIO ise alternatif olarak kullanılabilir. Dummy seçeneğinde ses çıkışı olmayacaktır.\n\nEmin değilseniz SDL2 seçeneğine ayarlayın.",
|
||||
"uk_UA": "Змінює серверну частину, яка використовується для відтворення аудіо.\n\nSDL2 є кращим, тоді як OpenAL і SoundIO використовуються як резервні варіанти. Dummy не матиме звуку.\n\nВстановіть SDL2, якщо не впевнені.",
|
||||
"zh_CN": "更改音频处理引擎。\n\n推荐选择“SDL2”,另外“OpenAL”和“SoundIO”可以作为备选,选择“无”将没有声音。\n\n如果不确定,请设置为“SDL2”。",
|
||||
"zh_TW": "變更用於繪製音訊的後端。\n\nSDL2 是首選,而 OpenAL 和 SoundIO 則作為備用。虛設 (Dummy) 將沒有聲音。\n\n如果不確定,請設定為 SDL2。"
|
||||
"en_US": "Changes the backend used to render audio.\n\nSDL3 is the preferred one, while OpenAL and SoundIO are used as fallbacks. Dummy will have no sound.\n\nSet to SDL3 if unsure.",
|
||||
"es_ES": "Cambia el motor usado para renderizar audio.\n\nSDL3 es el preferido, mientras que OpenAL y SoundIO se usan si hay problemas con este. Dummy no produce audio.\n\nSelecciona SDL3 si no sabes qué hacer.",
|
||||
"fr_FR": "Modifie la backend utilisé pour donner un rendu audio.\n\nSDL3 est recommandé, tandis que OpenAL et SoundIO sont utilisés en secours. Dummy ne produit aucun son.\n\nLaissez sur SDL3 si vous n'êtes pas sûr.",
|
||||
"he_IL": "משנה את אחראי השמע.\n\nSDL3 הוא הנבחר, למראת שOpenAL וגם SoundIO משומשים כאפשרויות חלופיות. אפשרות הDummy לא תשמיע קול כלל.\n\nמוטב להשאיר על SDL3 אם לא בטוחים.",
|
||||
"it_IT": "Cambia il backend usato per riprodurre l'audio.\n\nSDL3 è quello preferito, mentre OpenAL e SoundIO sono usati come ripiego. Dummy non riprodurrà alcun suono.\n\nNel dubbio, imposta l'opzione su SDL3.",
|
||||
"ja_JP": "音声レンダリングに使用するバックエンドを変更します.\n\nSDL3 が優先され, OpenAL と SoundIO はフォールバックとして使用されます. ダミーは音声出力しません.\n\nよくわからない場合は SDL3 を設定してください.",
|
||||
"ko_KR": "오디오 렌더링에 사용되는 백엔드를 변경합니다.\n\nSDL3가 선호되는 반면 OpenAL 및 SoundIO는 대체 수단으로 사용됩니다. 더미에는 소리가 나지 않습니다.\n\n모르면 SDL3로 설정하세요.",
|
||||
"no_NO": "Endrer backend brukt til å gjengi lyd.\n\nSDL3 er foretrukket, mens OpenAL og SoundIO brukes som reserveløsning. Dummy kommer ikke til å ha lyd.\n\nSett til SDL3 hvis usikker.",
|
||||
"pl_PL": "Zmienia backend używany do renderowania dźwięku.\n\nSDL3 jest preferowany, podczas gdy OpenAL i SoundIO są używane jako rezerwy. Dummy nie będzie odtwarzać dźwięku.\n\nW razie wątpliwości ustaw SDL3.",
|
||||
"pt_BR": "Altera o módulo usado para renderizar áudio.\n\nSDL3 é o preferido, enquanto OpenAL e SoundIO são usados como fallbacks. Dummy não terá som.\n\nDefina como SDL3 se não tiver certeza.",
|
||||
"ru_RU": "Меняет бэкенд используемый для воспроизведения аудио.\n\nSDL3 — предпочтительный вариант, в то время как OpenAL и SoundIO используются как резервные. Dummy не будет воспроизводить звук.\n\nРекомендуется использовать SDL3.",
|
||||
"sv_SE": "Ändrar bakänden som används för att rendera ljud.\n\nSDL3 är den föredragna, men OpenAL och SoundIO används för att falla tillbaka på. Dummy har inget ljud.\n\nStäll in till SDL3 om du är osäker.",
|
||||
"th_TH": "เปลี่ยนแบ็กเอนด์ที่ใช้ในการเรนเดอร์เสียง\n\nแนะนำเป็น SDL3 ในขณะที่ OpenAL และ SoundIO ถูกใช้เป็นทางเลือกสำรอง ดัมมี่จะไม่มีเสียง\n\nตั้งค่าเป็น SDL3 หากคุณไม่แน่ใจ",
|
||||
"tr_TR": "Ses çıkış motorunu değiştirir.\n\nSDL3 tercih edilen seçenektir, OpenAL ve SoundIO ise alternatif olarak kullanılabilir. Dummy seçeneğinde ses çıkışı olmayacaktır.\n\nEmin değilseniz SDL3 seçeneğine ayarlayın.",
|
||||
"uk_UA": "Змінює серверну частину, яка використовується для відтворення аудіо.\n\nSDL3 є кращим, тоді як OpenAL і SoundIO використовуються як резервні варіанти. Dummy не матиме звуку.\n\nВстановіть SDL3, якщо не впевнені.",
|
||||
"zh_CN": "更改音频处理引擎。\n\n推荐选择“SDL3”,另外“OpenAL”和“SoundIO”可以作为备选,选择“无”将没有声音。\n\n如果不确定,请设置为“SDL3”。",
|
||||
"zh_TW": "變更用於繪製音訊的後端。\n\nSDL3 是首選,而 OpenAL 和 SoundIO 則作為備用。虛設 (Dummy) 將沒有聲音。\n\n如果不確定,請設定為 SDL3。"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -19115,7 +19114,7 @@
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Виберіть ФАЙЛ, сумісний із Switch, для завантаження",
|
||||
"zh_CN": "请选择要加载的 Switch 兼容文件",
|
||||
"zh_TW": ""
|
||||
"zh_TW": "請選擇要載入的 Switch 相容檔案"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -19140,7 +19139,7 @@
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Виберіть РОЗПАКОВАНИЙ сумісний із Switch додаток для завантаження",
|
||||
"zh_CN": "请选择要加载的已解包的 Switch 兼容应用程序",
|
||||
"zh_TW": ""
|
||||
"zh_TW": "請選擇要載入的已解壓縮 Switch 相容應用程式"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -19165,7 +19164,7 @@
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Виберіть одну або кілька ПАПОК для масового завантаження оновлень титулів",
|
||||
"zh_CN": "请选择一个或多个文件夹来批量加载游戏更新",
|
||||
"zh_TW": ""
|
||||
"zh_TW": "請選擇一個或多個資料夾以批次載入遊戲更新"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -19190,7 +19189,7 @@
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Виберіть одну або кілька ПАПОК для масового завантаження DLC",
|
||||
"zh_CN": "请选择一个或多个文件夹来批量加载 DLC",
|
||||
"zh_TW": ""
|
||||
"zh_TW": "請選擇一個或多個資料夾以批次載入 DLC"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -19272,7 +19271,7 @@
|
||||
"ID": "SettingsTabHotkeys",
|
||||
"Translations": {
|
||||
"ar_SA": "اختصارات",
|
||||
"de_DE": "Hotkeys",
|
||||
"de_DE": "",
|
||||
"el_GR": "Συντομεύσεις",
|
||||
"en_US": "Hotkeys",
|
||||
"es_ES": "Atajos",
|
||||
@@ -19578,7 +19577,7 @@
|
||||
"es_ES": null,
|
||||
"fr_FR": null,
|
||||
"he_IL": "אמיבו",
|
||||
"it_IT": "Amiibo",
|
||||
"it_IT": "",
|
||||
"ja_JP": "",
|
||||
"ko_KR": null,
|
||||
"no_NO": "",
|
||||
@@ -23025,7 +23024,7 @@
|
||||
"de_DE": "Emulierte vertikale Synchronisation. \"Switch\" emuliert die 60Hz-Bildwiederholfrequenz der Switch. \"Unbounded\" ist eine unbegrenzte Bildwiederholfrequenz.",
|
||||
"el_GR": "",
|
||||
"en_US": "Emulated Vertical Sync. 'Switch' emulates the Switch's refresh rate of 60Hz. 'Unbounded' is an unbounded refresh rate.",
|
||||
"es_ES": "Sincronización vertical emulada. ‘Switch’ emula la frecuencia de actualización de la Switch de 60 Hz. ‘Sin límite’ es una frecuencia de actualización sin límite.",
|
||||
"es_ES": "Sincronización vertical emulada. ‘Switch’ emula la frecuencia de actualización de la Switch de 60\u202FHz. ‘Sin límite’ es una frecuencia de actualización sin límite.",
|
||||
"fr_FR": "VSync émulé. 'Switch' émule le taux de rafraîchissement de la Switch (60Hz). 'Sans Limite' est un taux de rafraîchissement qui n'est pas limité.",
|
||||
"he_IL": "",
|
||||
"it_IT": "Sincronizzazione verticale emulata. \"Switch\" emula la frequenza di aggiornamento di Nintendo Switch (60Hz). \"Nessun limite\" non impone alcun limite alla frequenza di aggiornamento.",
|
||||
@@ -23050,7 +23049,7 @@
|
||||
"de_DE": "Emulierte vertikale Synchronisation. \"Switch\" emuliert die 60Hz-Bildwiederholfrequenz der Switch. „Unbounded“ ist eine unbegrenzte Bildwiederholfrequenz. „Benutzerdefinierte Bildwiederholfrequenz“ emuliert die angegebene benutzerdefinierte Bildwiederholfrequenz.",
|
||||
"el_GR": "",
|
||||
"en_US": "Emulated Vertical Sync. 'Switch' emulates the Switch's refresh rate of 60Hz. 'Unbounded' is an unbounded refresh rate. 'Custom Refresh Rate' emulates the specified custom refresh rate.",
|
||||
"es_ES": "Sincronización Vertical Emulada. ‘Switch’ emula la frecuencia de actualización de la Switch de 60 Hz. ‘Sin límite’ es una frecuencia de actualización sin límite. ‘Frecuencia de actualización personalizada’ emula la frecuencia de actualización personalizada especificada.",
|
||||
"es_ES": "Sincronización Vertical Emulada. ‘Switch’ emula la frecuencia de actualización de la Switch de 60\u202FHz. ‘Sin límite’ es una frecuencia de actualización sin límite. ‘Frecuencia de actualización personalizada’ emula la frecuencia de actualización personalizada especificada.",
|
||||
"fr_FR": "VSync émulé. 'Switch' émule le taux de rafraîchissement de la Switch (60Hz). 'Sans Limite' est un taux de rafraîchissement qui n'est pas limité. 'Taux de Rafraîchissement Customisé' émule le taux de rafraîchissement spécifié.",
|
||||
"he_IL": "",
|
||||
"it_IT": "Sincronizzazione verticale emulata. \"Switch\" emula la frequenza di aggiornamento di Nintendo Switch (60Hz). \"Nessun limite\" non impone alcun limite alla frequenza di aggiornamento. \"Frequenza di aggiornamento personalizzata\" emula la frequenza di aggiornamento specificata.",
|
||||
@@ -24040,7 +24039,7 @@
|
||||
"tr_TR": "",
|
||||
"uk_UA": "",
|
||||
"zh_CN": "调试",
|
||||
"zh_TW": "除錯"
|
||||
"zh_TW": "偵錯"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -24065,7 +24064,7 @@
|
||||
"tr_TR": "",
|
||||
"uk_UA": "",
|
||||
"zh_CN": "调试",
|
||||
"zh_TW": "除錯"
|
||||
"zh_TW": "偵錯"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -24140,7 +24139,7 @@
|
||||
"tr_TR": "",
|
||||
"uk_UA": "",
|
||||
"zh_CN": "启用 GDB stub 使得可以调试正在运行的应用程序。仅限开发用途!",
|
||||
"zh_TW": "啟用 GDB stub 可利用 gdb 除錯正在執行的應用程式。僅供開發使用!"
|
||||
"zh_TW": "啟用 GDB stub 可利用 gdb 偵錯正在執行的應用程式。僅供開發使用!"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -24215,7 +24214,7 @@
|
||||
"tr_TR": "",
|
||||
"uk_UA": "",
|
||||
"zh_CN": "在执行首条指令前挂起应用程序,这样就可以从最早的点开始调试。",
|
||||
"zh_TW": "在執行首項指令前暫停應用程式,以便從最早的點開始除錯。"
|
||||
"zh_TW": "在執行首項指令前暫停應用程式,以便從最早的點開始偵錯。"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -24844,4 +24843,4 @@
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
SCRIPT_DIR=$(dirname "$(realpath "$0")")
|
||||
|
||||
if [ -f "$SCRIPT_DIR/Ryujinx.Headless.SDL2" ]; then
|
||||
RYUJINX_BIN="Ryujinx.Headless.SDL2"
|
||||
if [ -f "$SCRIPT_DIR/Ryujinx.Headless.SDL3" ]; then
|
||||
RYUJINX_BIN="Ryujinx.Headless.SDL3"
|
||||
fi
|
||||
|
||||
if [ -f "$SCRIPT_DIR/Ryujinx" ]; then
|
||||
|
||||
@@ -43,7 +43,7 @@ fi
|
||||
ARM64_OUTPUT="$TEMP_DIRECTORY/publish_arm64"
|
||||
X64_OUTPUT="$TEMP_DIRECTORY/publish_x64"
|
||||
UNIVERSAL_OUTPUT="$OUTPUT_DIRECTORY/publish"
|
||||
EXECUTABLE_SUB_PATH=Ryujinx.Headless.SDL2
|
||||
EXECUTABLE_SUB_PATH=Ryujinx.Headless.SDL3
|
||||
|
||||
rm -rf "$TEMP_DIRECTORY"
|
||||
mkdir -p "$TEMP_DIRECTORY"
|
||||
@@ -51,9 +51,9 @@ 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
|
||||
dotnet build -c "$CONFIGURATION" src/Ryujinx.Headless.SDL3
|
||||
dotnet publish -c "$CONFIGURATION" -r osx-arm64 -o "$TEMP_DIRECTORY/publish_arm64" "${DOTNET_COMMON_ARGS[@]}" src/Ryujinx.Headless.SDL3
|
||||
dotnet publish -c "$CONFIGURATION" -r osx-x64 -o "$TEMP_DIRECTORY/publish_x64" "${DOTNET_COMMON_ARGS[@]}" src/Ryujinx.Headless.SDL3
|
||||
|
||||
# Get rid of the support library for ARMeilleure for x64 (that's only for arm64)
|
||||
rm -rf "$TEMP_DIRECTORY/publish_x64/libarmeilleure-jitsupport.dylib"
|
||||
@@ -115,8 +115,8 @@ 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"
|
||||
tar --exclude "publish/Ryujinx.Headless.SDL3" -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.SDL3" "publish/Ryujinx.Headless.SDL3"
|
||||
gzip -9 < "$RELEASE_TAR_FILE_NAME" > "$RELEASE_TAR_FILE_NAME.gz"
|
||||
rm "$RELEASE_TAR_FILE_NAME"
|
||||
popd
|
||||
|
||||
@@ -8,7 +8,7 @@ Intro to Ryujinx
|
||||
Ryujinx is an open-source Nintendo Switch emulator, created by gdkchan, written in C#.
|
||||
* The CPU emulator, ARMeilleure, emulates an ARMv8 CPU and currently has support for most 64-bit ARMv8 and some of the ARMv7 (and older) instructions.
|
||||
* The GPU emulator emulates the Switch's Maxwell GPU using either the OpenGL (version 4.5 minimum), Vulkan, or Metal (via MoltenVK) APIs through a custom build of OpenTK or Silk.NET respectively.
|
||||
* Audio output is entirely supported via C# wrappers for SDL2, with OpenAL & libsoundio as fallbacks.
|
||||
* Audio output is entirely supported via C# wrappers for SDL3, with OpenAL & libsoundio as fallbacks.
|
||||
|
||||
Getting Started
|
||||
===============
|
||||
|
||||
@@ -1107,17 +1107,17 @@ namespace ARMeilleure.CodeGen.X86
|
||||
}
|
||||
else
|
||||
{
|
||||
if (flags.HasFlag(InstructionFlags.Prefix66))
|
||||
if ((flags & InstructionFlags.Prefix66) != 0)
|
||||
{
|
||||
WriteByte(0x66);
|
||||
}
|
||||
|
||||
if (flags.HasFlag(InstructionFlags.PrefixF2))
|
||||
if ((flags & InstructionFlags.PrefixF2) != 0f)
|
||||
{
|
||||
WriteByte(0xf2);
|
||||
}
|
||||
|
||||
if (flags.HasFlag(InstructionFlags.PrefixF3))
|
||||
if ((flags & InstructionFlags.PrefixF3) != 0f)
|
||||
{
|
||||
WriteByte(0xf3);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ryujinx.Audio\Ryujinx.Audio.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.SDL2.Common\Ryujinx.SDL2.Common.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.SDL3.Common\Ryujinx.SDL3.Common.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,12 +1,12 @@
|
||||
namespace Ryujinx.Audio.Backends.SDL2
|
||||
namespace Ryujinx.Audio.Backends.SDL3
|
||||
{
|
||||
class SDL2AudioBuffer
|
||||
class SDL3AudioBuffer
|
||||
{
|
||||
public readonly ulong DriverIdentifier;
|
||||
public readonly ulong SampleCount;
|
||||
public ulong SamplePlayed;
|
||||
|
||||
public SDL2AudioBuffer(ulong driverIdentifier, ulong sampleCount)
|
||||
public SDL3AudioBuffer(ulong driverIdentifier, ulong sampleCount)
|
||||
{
|
||||
DriverIdentifier = driverIdentifier;
|
||||
SampleCount = sampleCount;
|
||||
@@ -2,42 +2,41 @@ using Ryujinx.Audio.Common;
|
||||
using Ryujinx.Audio.Integration;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Memory;
|
||||
using Ryujinx.SDL2.Common;
|
||||
using Ryujinx.SDL3.Common;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using static Ryujinx.Audio.Integration.IHardwareDeviceDriver;
|
||||
using static SDL2.SDL;
|
||||
using SDL;
|
||||
using static SDL.SDL3;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Audio.Backends.SDL2
|
||||
|
||||
namespace Ryujinx.Audio.Backends.SDL3
|
||||
{
|
||||
public class SDL2HardwareDeviceDriver : IHardwareDeviceDriver
|
||||
|
||||
using unsafe SDL_AudioStreamCallbackPointer = delegate* unmanaged[Cdecl]<nint, SDL_AudioStream*, int, int, void>;
|
||||
|
||||
public class SDL3HardwareDeviceDriver : IHardwareDeviceDriver
|
||||
{
|
||||
private readonly ManualResetEvent _updateRequiredEvent;
|
||||
private readonly ManualResetEvent _pauseEvent;
|
||||
private readonly ConcurrentDictionary<SDL2HardwareDeviceSession, byte> _sessions;
|
||||
private readonly ConcurrentDictionary<SDL3HardwareDeviceSession, byte> _sessions;
|
||||
|
||||
private readonly bool _supportSurroundConfiguration;
|
||||
|
||||
public float Volume { get; set; }
|
||||
|
||||
// TODO: Add this to SDL2-CS
|
||||
// NOTE: We use a DllImport here because of marshaling issue for spec.
|
||||
[DllImport("SDL2")]
|
||||
private static extern int SDL_GetDefaultAudioInfo(nint name, out SDL_AudioSpec spec, int isCapture);
|
||||
|
||||
public SDL2HardwareDeviceDriver()
|
||||
public unsafe SDL3HardwareDeviceDriver()
|
||||
{
|
||||
_updateRequiredEvent = new ManualResetEvent(false);
|
||||
_pauseEvent = new ManualResetEvent(true);
|
||||
_sessions = new ConcurrentDictionary<SDL2HardwareDeviceSession, byte>();
|
||||
_sessions = new ConcurrentDictionary<SDL3HardwareDeviceSession, byte>();
|
||||
|
||||
SDL2Driver.Instance.Initialize();
|
||||
SDL3Driver.Instance.Initialize();
|
||||
|
||||
int res = SDL_GetDefaultAudioInfo(nint.Zero, out SDL_AudioSpec spec, 0);
|
||||
|
||||
if (res != 0)
|
||||
SDL_AudioSpec spec;
|
||||
if (!SDL_GetAudioDeviceFormat(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &spec, null))
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application,
|
||||
$"SDL_GetDefaultAudioInfo failed with error \"{SDL_GetError()}\"");
|
||||
@@ -54,16 +53,16 @@ namespace Ryujinx.Audio.Backends.SDL2
|
||||
|
||||
public static bool IsSupported => IsSupportedInternal();
|
||||
|
||||
private static bool IsSupportedInternal()
|
||||
private unsafe static bool IsSupportedInternal()
|
||||
{
|
||||
uint device = OpenStream(SampleFormat.PcmInt16, Constants.TargetSampleRate, Constants.ChannelCountMax, Constants.TargetSampleCount, null);
|
||||
SDL_AudioStream* device = OpenStream(SampleFormat.PcmInt16, Constants.TargetSampleRate, Constants.ChannelCountMax, Constants.TargetSampleCount, null);
|
||||
|
||||
if (device != 0)
|
||||
if (device != null)
|
||||
{
|
||||
SDL_CloseAudioDevice(device);
|
||||
SDL_DestroyAudioStream(device);
|
||||
}
|
||||
|
||||
return device != 0;
|
||||
return device != null;
|
||||
}
|
||||
|
||||
public ManualResetEvent GetUpdateRequiredEvent()
|
||||
@@ -90,67 +89,68 @@ namespace Ryujinx.Audio.Backends.SDL2
|
||||
|
||||
if (direction != Direction.Output)
|
||||
{
|
||||
throw new NotImplementedException("Input direction is currently not implemented on SDL2 backend!");
|
||||
throw new NotImplementedException("Input direction is currently not implemented on SDL3 backend!");
|
||||
}
|
||||
|
||||
SDL2HardwareDeviceSession session = new(this, memoryManager, sampleFormat, sampleRate, channelCount);
|
||||
SDL3HardwareDeviceSession session = new(this, memoryManager, sampleFormat, sampleRate, channelCount);
|
||||
|
||||
_sessions.TryAdd(session, 0);
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
internal bool Unregister(SDL2HardwareDeviceSession session)
|
||||
internal bool Unregister(SDL3HardwareDeviceSession session)
|
||||
{
|
||||
return _sessions.TryRemove(session, out _);
|
||||
}
|
||||
|
||||
private static SDL_AudioSpec GetSDL2Spec(SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount, uint sampleCount)
|
||||
private static SDL_AudioSpec GetSDL3Spec(SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount)
|
||||
{
|
||||
return new SDL_AudioSpec
|
||||
{
|
||||
channels = (byte)requestedChannelCount,
|
||||
format = GetSDL2Format(requestedSampleFormat),
|
||||
format = GetSDL3Format(requestedSampleFormat),
|
||||
freq = (int)requestedSampleRate,
|
||||
samples = (ushort)sampleCount,
|
||||
};
|
||||
}
|
||||
|
||||
internal static ushort GetSDL2Format(SampleFormat format)
|
||||
internal static SDL_AudioFormat GetSDL3Format(SampleFormat format)
|
||||
{
|
||||
return format switch
|
||||
{
|
||||
SampleFormat.PcmInt8 => AUDIO_S8,
|
||||
SampleFormat.PcmInt16 => AUDIO_S16,
|
||||
SampleFormat.PcmInt32 => AUDIO_S32,
|
||||
SampleFormat.PcmFloat => AUDIO_F32,
|
||||
SampleFormat.PcmInt8 => SDL_AudioFormat.SDL_AUDIO_S8,
|
||||
SampleFormat.PcmInt16 => SDL_AudioFormat.SDL_AUDIO_S16LE,
|
||||
SampleFormat.PcmInt32 => SDL_AudioFormat.SDL_AUDIO_S32LE,
|
||||
SampleFormat.PcmFloat => SDL_AudioFormat.SDL_AUDIO_F32LE,
|
||||
_ => throw new ArgumentException($"Unsupported sample format {format}"),
|
||||
};
|
||||
}
|
||||
|
||||
internal static uint OpenStream(SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount, uint sampleCount, SDL_AudioCallback callback)
|
||||
internal unsafe static SDL_AudioStream* OpenStream(SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount, uint sampleCount, SDL3HardwareDeviceSession.SDL_AudioStreamCallback callback)
|
||||
{
|
||||
SDL_AudioSpec desired = GetSDL2Spec(requestedSampleFormat, requestedSampleRate, requestedChannelCount, sampleCount);
|
||||
SDL_AudioSpec desired = GetSDL3Spec(requestedSampleFormat, requestedSampleRate, requestedChannelCount);
|
||||
SDL_AudioSpec got = desired;
|
||||
var pCallback = callback != null ? (SDL_AudioStreamCallbackPointer)Marshal.GetFunctionPointerForDelegate(callback) : null;
|
||||
|
||||
desired.callback = callback;
|
||||
// From SDL 3 and on, SDL requires us to set this as a hint
|
||||
SDL_SetHint(SDL_HINT_AUDIO_DEVICE_SAMPLE_FRAMES, $"{sampleCount}");
|
||||
SDL_AudioStream* device = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &got, pCallback, 0);
|
||||
|
||||
uint device = SDL_OpenAudioDevice(nint.Zero, 0, ref desired, out SDL_AudioSpec got, 0);
|
||||
|
||||
if (device == 0)
|
||||
if (device == null)
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application, $"SDL2 open audio device initialization failed with error \"{SDL_GetError()}\"");
|
||||
Logger.Error?.Print(LogClass.Application, $"SDL3 open audio device initialization failed with error \"{SDL_GetError()}\"");
|
||||
|
||||
return 0;
|
||||
return null;
|
||||
}
|
||||
|
||||
bool isValid = got.format == desired.format && got.freq == desired.freq && got.channels == desired.channels;
|
||||
|
||||
if (!isValid)
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application, "SDL2 open audio device is not valid");
|
||||
SDL_CloseAudioDevice(device);
|
||||
Logger.Error?.Print(LogClass.Application, "SDL3 open audio device is not valid");
|
||||
SDL_DestroyAudioStream(device);
|
||||
|
||||
return 0;
|
||||
return null;
|
||||
}
|
||||
|
||||
return device;
|
||||
@@ -166,12 +166,12 @@ namespace Ryujinx.Audio.Backends.SDL2
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
foreach (SDL2HardwareDeviceSession session in _sessions.Keys)
|
||||
foreach (SDL3HardwareDeviceSession session in _sessions.Keys)
|
||||
{
|
||||
session.Dispose();
|
||||
}
|
||||
|
||||
SDL2Driver.Instance.Dispose();
|
||||
SDL3Driver.Instance.Dispose();
|
||||
|
||||
_pauseEvent.Dispose();
|
||||
}
|
||||
@@ -6,36 +6,43 @@ using Ryujinx.Memory;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading;
|
||||
using SDL;
|
||||
using static SDL.SDL3;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using static SDL2.SDL;
|
||||
|
||||
namespace Ryujinx.Audio.Backends.SDL2
|
||||
namespace Ryujinx.Audio.Backends.SDL3
|
||||
{
|
||||
class SDL2HardwareDeviceSession : HardwareDeviceSessionOutputBase
|
||||
|
||||
|
||||
|
||||
unsafe class SDL3HardwareDeviceSession : HardwareDeviceSessionOutputBase
|
||||
{
|
||||
private readonly SDL2HardwareDeviceDriver _driver;
|
||||
private readonly ConcurrentQueue<SDL2AudioBuffer> _queuedBuffers;
|
||||
private readonly SDL3HardwareDeviceDriver _driver;
|
||||
private readonly ConcurrentQueue<SDL3AudioBuffer> _queuedBuffers;
|
||||
private readonly DynamicRingBuffer _ringBuffer;
|
||||
private ulong _playedSampleCount;
|
||||
private readonly ManualResetEvent _updateRequiredEvent;
|
||||
private uint _outputStream;
|
||||
private SDL_AudioStream* _outputStream;
|
||||
private bool _hasSetupError;
|
||||
private readonly SDL_AudioCallback _callbackDelegate;
|
||||
private readonly SDL_AudioStreamCallback _callbackDelegate;
|
||||
private readonly int _bytesPerFrame;
|
||||
private uint _sampleCount;
|
||||
private bool _started;
|
||||
private float _volume;
|
||||
private readonly ushort _nativeSampleFormat;
|
||||
private readonly SDL_AudioFormat _nativeSampleFormat;
|
||||
|
||||
public SDL2HardwareDeviceSession(SDL2HardwareDeviceDriver driver, IVirtualMemoryManager memoryManager, SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount) : base(memoryManager, requestedSampleFormat, requestedSampleRate, requestedChannelCount)
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
internal delegate void SDL_AudioStreamCallback(nint session, SDL_AudioStream* stream, int stream_count, int device_count);
|
||||
|
||||
public SDL3HardwareDeviceSession(SDL3HardwareDeviceDriver driver, IVirtualMemoryManager memoryManager, SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount) : base(memoryManager, requestedSampleFormat, requestedSampleRate, requestedChannelCount)
|
||||
{
|
||||
_driver = driver;
|
||||
_updateRequiredEvent = _driver.GetUpdateRequiredEvent();
|
||||
_queuedBuffers = new ConcurrentQueue<SDL2AudioBuffer>();
|
||||
_queuedBuffers = new ConcurrentQueue<SDL3AudioBuffer>();
|
||||
_ringBuffer = new DynamicRingBuffer();
|
||||
_callbackDelegate = Update;
|
||||
_bytesPerFrame = BackendHelper.GetSampleSize(RequestedSampleFormat) * (int)RequestedChannelCount;
|
||||
_nativeSampleFormat = SDL2HardwareDeviceDriver.GetSDL2Format(RequestedSampleFormat);
|
||||
_nativeSampleFormat = SDL3HardwareDeviceDriver.GetSDL3Format(RequestedSampleFormat);
|
||||
_sampleCount = uint.MaxValue;
|
||||
_started = false;
|
||||
_volume = 1f;
|
||||
@@ -44,45 +51,51 @@ namespace Ryujinx.Audio.Backends.SDL2
|
||||
private void EnsureAudioStreamSetup(AudioBuffer buffer)
|
||||
{
|
||||
uint bufferSampleCount = (uint)GetSampleCount(buffer);
|
||||
bool needAudioSetup = (_outputStream == 0 && !_hasSetupError) ||
|
||||
bool needAudioSetup = (_outputStream == null && !_hasSetupError) ||
|
||||
(bufferSampleCount >= Constants.TargetSampleCount && bufferSampleCount < _sampleCount);
|
||||
|
||||
if (needAudioSetup)
|
||||
{
|
||||
_sampleCount = Math.Max(Constants.TargetSampleCount, bufferSampleCount);
|
||||
|
||||
uint newOutputStream = SDL2HardwareDeviceDriver.OpenStream(RequestedSampleFormat, RequestedSampleRate, RequestedChannelCount, _sampleCount, _callbackDelegate);
|
||||
SDL_AudioStream* newOutputStream = SDL3HardwareDeviceDriver.OpenStream(RequestedSampleFormat, RequestedSampleRate, RequestedChannelCount, _sampleCount, _callbackDelegate);
|
||||
|
||||
_hasSetupError = newOutputStream == 0;
|
||||
_hasSetupError = newOutputStream == null;
|
||||
|
||||
if (!_hasSetupError)
|
||||
{
|
||||
if (_outputStream != 0)
|
||||
if (_outputStream != null)
|
||||
{
|
||||
SDL_CloseAudioDevice(_outputStream);
|
||||
SDL_DestroyAudioStream(_outputStream);
|
||||
}
|
||||
|
||||
_outputStream = newOutputStream;
|
||||
|
||||
SDL_PauseAudioDevice(_outputStream, _started ? 0 : 1);
|
||||
if (_started) {
|
||||
SDL_ResumeAudioStreamDevice(_outputStream);
|
||||
} else {
|
||||
SDL_PauseAudioStreamDevice(_outputStream);
|
||||
}
|
||||
|
||||
Logger.Info?.Print(LogClass.Audio, $"New audio stream setup with a target sample count of {_sampleCount}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe void Update(nint userdata, nint stream, int streamLength)
|
||||
private unsafe void Update(nint userdata, SDL_AudioStream* streamDevice, int additionalAmount, int totalAmmount)
|
||||
{
|
||||
Span<byte> streamSpan = new((void*)stream, streamLength);
|
||||
using SpanOwner<byte> stream = SpanOwner<byte>.Rent(additionalAmount);
|
||||
Span<byte> streamSpan = stream.Span;
|
||||
|
||||
int maxFrameCount = (int)GetSampleCount(streamLength);
|
||||
|
||||
int maxFrameCount = (int)GetSampleCount(additionalAmount);
|
||||
int bufferedFrames = _ringBuffer.Length / _bytesPerFrame;
|
||||
|
||||
int frameCount = Math.Min(bufferedFrames, maxFrameCount);
|
||||
|
||||
if (frameCount == 0)
|
||||
{
|
||||
// SDL2 left the responsibility to the user to clear the buffer.
|
||||
// SDL3 left the responsibility to the user to clear the buffer.
|
||||
streamSpan.Clear();
|
||||
|
||||
return;
|
||||
@@ -94,15 +107,17 @@ namespace Ryujinx.Audio.Backends.SDL2
|
||||
|
||||
_ringBuffer.Read(samples, 0, samples.Length);
|
||||
|
||||
fixed (byte* p = samples)
|
||||
{
|
||||
nint pStreamSrc = (nint)p;
|
||||
// Zero the dest buffer
|
||||
streamSpan.Clear();
|
||||
|
||||
// Zero the dest buffer
|
||||
streamSpan.Clear();
|
||||
fixed (byte* pStreamDst = streamSpan) {
|
||||
fixed (byte* pStreamSrc = samples)
|
||||
{
|
||||
|
||||
// Apply volume to written data
|
||||
SDL_MixAudioFormat(stream, pStreamSrc, _nativeSampleFormat, (uint)samples.Length, (int)(_driver.Volume * _volume * SDL_MIX_MAXVOLUME));
|
||||
// Apply volume to written data
|
||||
SDL_MixAudio(pStreamDst, pStreamSrc, _nativeSampleFormat, (uint)samples.Length, _driver.Volume * _volume);
|
||||
SDL_PutAudioStreamData(streamDevice, (nint)pStreamDst, additionalAmount);
|
||||
}
|
||||
}
|
||||
|
||||
ulong sampleCount = GetSampleCount(samples.Length);
|
||||
@@ -111,7 +126,7 @@ namespace Ryujinx.Audio.Backends.SDL2
|
||||
|
||||
bool needUpdate = false;
|
||||
|
||||
while (availaibleSampleCount > 0 && _queuedBuffers.TryPeek(out SDL2AudioBuffer driverBuffer))
|
||||
while (availaibleSampleCount > 0 && _queuedBuffers.TryPeek(out SDL3AudioBuffer driverBuffer))
|
||||
{
|
||||
ulong sampleStillNeeded = driverBuffer.SampleCount - Interlocked.Read(ref driverBuffer.SamplePlayed);
|
||||
ulong playedAudioBufferSampleCount = Math.Min(sampleStillNeeded, availaibleSampleCount);
|
||||
@@ -152,9 +167,9 @@ namespace Ryujinx.Audio.Backends.SDL2
|
||||
{
|
||||
EnsureAudioStreamSetup(buffer);
|
||||
|
||||
if (_outputStream != 0)
|
||||
if (_outputStream != null)
|
||||
{
|
||||
SDL2AudioBuffer driverBuffer = new(buffer.DataPointer, GetSampleCount(buffer));
|
||||
SDL3AudioBuffer driverBuffer = new(buffer.DataPointer, GetSampleCount(buffer));
|
||||
|
||||
_ringBuffer.Write(buffer.Data, 0, buffer.Data.Length);
|
||||
|
||||
@@ -177,9 +192,9 @@ namespace Ryujinx.Audio.Backends.SDL2
|
||||
{
|
||||
if (!_started)
|
||||
{
|
||||
if (_outputStream != 0)
|
||||
if (_outputStream != null)
|
||||
{
|
||||
SDL_PauseAudioDevice(_outputStream, 0);
|
||||
SDL_ResumeAudioStreamDevice(_outputStream);
|
||||
}
|
||||
|
||||
_started = true;
|
||||
@@ -190,9 +205,9 @@ namespace Ryujinx.Audio.Backends.SDL2
|
||||
{
|
||||
if (_started)
|
||||
{
|
||||
if (_outputStream != 0)
|
||||
if (_outputStream != null)
|
||||
{
|
||||
SDL_PauseAudioDevice(_outputStream, 1);
|
||||
SDL_PauseAudioStreamDevice(_outputStream);
|
||||
}
|
||||
|
||||
_started = false;
|
||||
@@ -203,7 +218,7 @@ namespace Ryujinx.Audio.Backends.SDL2
|
||||
|
||||
public override bool WasBufferFullyConsumed(AudioBuffer buffer)
|
||||
{
|
||||
if (!_queuedBuffers.TryPeek(out SDL2AudioBuffer driverBuffer))
|
||||
if (!_queuedBuffers.TryPeek(out SDL3AudioBuffer driverBuffer))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -218,9 +233,9 @@ namespace Ryujinx.Audio.Backends.SDL2
|
||||
PrepareToClose();
|
||||
Stop();
|
||||
|
||||
if (_outputStream != 0)
|
||||
if (_outputStream != null)
|
||||
{
|
||||
SDL_CloseAudioDevice(_outputStream);
|
||||
SDL_DestroyAudioStream(_outputStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using Ryujinx.Audio.Integration;
|
||||
using Ryujinx.Audio.Renderer.Server.Sink;
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
|
||||
@@ -35,7 +36,9 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||
Enabled = true;
|
||||
NodeId = nodeId;
|
||||
|
||||
DeviceName = Encoding.ASCII.GetString(sink.Parameter.DeviceName).TrimEnd('\0');
|
||||
// Unused and wasting time and memory, re-add if needed
|
||||
// DeviceName = Encoding.ASCII.GetString(sink.Parameter.DeviceName).TrimEnd('\0');
|
||||
|
||||
SessionId = sessionId;
|
||||
InputCount = sink.Parameter.InputCount;
|
||||
InputBufferIndices = new ushort[InputCount];
|
||||
@@ -88,7 +91,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||
inputCount = bufferCount;
|
||||
}
|
||||
|
||||
short[] outputBuffer = new short[inputCount * SampleCount];
|
||||
short[] outputBuffer = ArrayPool<short>.Shared.Rent((int)inputCount * SampleCount);
|
||||
|
||||
for (int i = 0; i < bufferCount; i++)
|
||||
{
|
||||
@@ -100,7 +103,9 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||
}
|
||||
}
|
||||
|
||||
device.AppendBuffer(outputBuffer, inputCount);
|
||||
device.AppendBuffer(outputBuffer.AsSpan(..((int)inputCount * SampleCount)), inputCount);
|
||||
|
||||
ArrayPool<short>.Shared.Return(outputBuffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -188,6 +188,8 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
|
||||
/// </summary>
|
||||
public Span<bool> BiquadFilterNeedInitialization => SpanHelpers.AsSpan<BiquadFilterNeedInitializationArrayStruct, bool>(ref _biquadFilterNeedInitialization);
|
||||
|
||||
private static List<ErrorInfo> _waveBufferUpdaterErrorInfosList;
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the <see cref="VoiceInfo"/>.
|
||||
/// </summary>
|
||||
@@ -216,6 +218,8 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
|
||||
DataSourceStateAddressInfo.Setup(0, 0);
|
||||
|
||||
InitializeWaveBuffers();
|
||||
|
||||
_waveBufferUpdaterErrorInfosList ??= [];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -587,14 +591,14 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
|
||||
|
||||
Span<WaveBuffer> waveBuffersSpan = WaveBuffers.AsSpan();
|
||||
Span<WaveBufferInternal> pWaveBuffersSpan = parameter.WaveBuffers.AsSpan();
|
||||
List<ErrorInfo> errorInfosList = [];
|
||||
_waveBufferUpdaterErrorInfosList.Clear();
|
||||
|
||||
for (int i = 0; i < Constants.VoiceWaveBufferCount; i++)
|
||||
{
|
||||
UpdateWaveBuffer(errorInfosList, ref waveBuffersSpan[i], ref pWaveBuffersSpan[i], parameter.SampleFormat, voiceState.IsWaveBufferValid[i], mapper, ref behaviourInfo);
|
||||
UpdateWaveBuffer(_waveBufferUpdaterErrorInfosList, ref waveBuffersSpan[i], ref pWaveBuffersSpan[i], parameter.SampleFormat, voiceState.IsWaveBufferValid[i], mapper, ref behaviourInfo);
|
||||
}
|
||||
|
||||
errorInfos = errorInfosList.ToArray();
|
||||
errorInfos = _waveBufferUpdaterErrorInfosList.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -628,14 +632,14 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
|
||||
|
||||
Span<WaveBuffer> waveBuffersSpan = WaveBuffers.AsSpan();
|
||||
Span<WaveBufferInternal> pWaveBuffersSpan = parameter.WaveBuffers.AsSpan();
|
||||
List<ErrorInfo> errorInfosList = [];
|
||||
_waveBufferUpdaterErrorInfosList.Clear();
|
||||
|
||||
for (int i = 0; i < Constants.VoiceWaveBufferCount; i++)
|
||||
{
|
||||
UpdateWaveBuffer(errorInfosList, ref waveBuffersSpan[i], ref pWaveBuffersSpan[i], parameter.SampleFormat, voiceState.IsWaveBufferValid[i], mapper, ref behaviourInfo);
|
||||
UpdateWaveBuffer(_waveBufferUpdaterErrorInfosList, ref waveBuffersSpan[i], ref pWaveBuffersSpan[i], parameter.SampleFormat, voiceState.IsWaveBufferValid[i], mapper, ref behaviourInfo);
|
||||
}
|
||||
|
||||
errorInfos = errorInfosList.ToArray();
|
||||
errorInfos = _waveBufferUpdaterErrorInfosList.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -24,7 +24,10 @@ namespace Ryujinx.Common.Collections
|
||||
/// <exception cref="ArgumentNullException"><paramref name="key"/> is null</exception>
|
||||
public int Get(TKey key, ref TValue[] overlaps)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(key);
|
||||
if (!typeof(TKey).IsValueType)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(key);
|
||||
}
|
||||
|
||||
IntervalTreeNode<TKey, TValue> node = GetNode(key);
|
||||
|
||||
@@ -91,7 +94,10 @@ namespace Ryujinx.Common.Collections
|
||||
/// <returns>Number of deleted values</returns>
|
||||
public int Remove(TKey key, TValue value)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(key);
|
||||
if (!typeof(TKey).IsValueType)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(key);
|
||||
}
|
||||
|
||||
int removed = Delete(key, value);
|
||||
|
||||
@@ -144,7 +150,10 @@ namespace Ryujinx.Common.Collections
|
||||
/// <exception cref="ArgumentNullException"><paramref name="key"/> is null</exception>
|
||||
private IntervalTreeNode<TKey, TValue> GetNode(TKey key)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(key);
|
||||
if (!typeof(TKey).IsValueType)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(key);
|
||||
}
|
||||
|
||||
IntervalTreeNode<TKey, TValue> node = Root;
|
||||
while (node != null)
|
||||
|
||||
@@ -8,6 +8,7 @@ namespace Ryujinx.Common.Configuration.Hid
|
||||
{
|
||||
Invalid,
|
||||
WindowKeyboard,
|
||||
GamepadSDL2,
|
||||
GamepadSDL2, //backcompat
|
||||
GamepadSDL3,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ namespace Ryujinx.Common.Configuration.Hid
|
||||
return backendType switch
|
||||
{
|
||||
InputBackendType.WindowKeyboard => JsonSerializer.Deserialize(ref reader, _serializerContext.StandardKeyboardInputConfig),
|
||||
InputBackendType.GamepadSDL2 => JsonSerializer.Deserialize(ref reader, _serializerContext.StandardControllerInputConfig),
|
||||
InputBackendType.GamepadSDL2 or InputBackendType.GamepadSDL3 => JsonSerializer.Deserialize(ref reader, _serializerContext.StandardControllerInputConfig),
|
||||
_ => throw new InvalidOperationException($"Unknown backend type {backendType}"),
|
||||
};
|
||||
}
|
||||
@@ -70,7 +70,7 @@ namespace Ryujinx.Common.Configuration.Hid
|
||||
case InputBackendType.WindowKeyboard:
|
||||
JsonSerializer.Serialize(writer, value as StandardKeyboardInputConfig, _serializerContext.StandardKeyboardInputConfig);
|
||||
break;
|
||||
case InputBackendType.GamepadSDL2:
|
||||
case InputBackendType.GamepadSDL2 or InputBackendType.GamepadSDL3:
|
||||
JsonSerializer.Serialize(writer, value as StandardControllerInputConfig, _serializerContext.StandardControllerInputConfig);
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
using Ryujinx.Graphics.GAL.Multithreading.Model;
|
||||
using Ryujinx.Graphics.GAL.Multithreading.Resources;
|
||||
using System.Linq;
|
||||
using System.Buffers;
|
||||
|
||||
namespace Ryujinx.Graphics.GAL.Multithreading.Commands
|
||||
{
|
||||
struct SetRenderTargetsCommand : IGALCommand, IGALCommand<SetRenderTargetsCommand>
|
||||
{
|
||||
public static readonly ArrayPool<ITexture> ArrayPool = ArrayPool<ITexture>.Create(512, 50);
|
||||
public readonly CommandType CommandType => CommandType.SetRenderTargets;
|
||||
private TableRef<ITexture[]> _colors;
|
||||
private TableRef<ITexture> _depthStencil;
|
||||
@@ -18,7 +19,18 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands
|
||||
|
||||
public static void Run(ref SetRenderTargetsCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
||||
{
|
||||
renderer.Pipeline.SetRenderTargets(command._colors.Get(threaded).Select(color => ((ThreadedTexture)color)?.Base).ToArray(), command._depthStencil.GetAs<ThreadedTexture>(threaded)?.Base);
|
||||
ITexture[] colors = command._colors.Get(threaded);
|
||||
ITexture[] colorsCopy = ArrayPool.Rent(colors.Length);
|
||||
|
||||
for (int i = 0; i < colors.Length; i++)
|
||||
{
|
||||
colorsCopy[i] = ((ThreadedTexture)colors[i])?.Base;
|
||||
}
|
||||
|
||||
renderer.Pipeline.SetRenderTargets(colorsCopy, command._depthStencil.GetAs<ThreadedTexture>(threaded)?.Base);
|
||||
|
||||
ArrayPool.Return(colorsCopy);
|
||||
ArrayPool.Return(colors);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -269,7 +269,10 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
||||
|
||||
public unsafe void SetRenderTargets(ITexture[] colors, ITexture depthStencil)
|
||||
{
|
||||
_renderer.New<SetRenderTargetsCommand>()->Set(Ref(colors.ToArray()), Ref(depthStencil));
|
||||
ITexture[] colorsCopy = SetRenderTargetsCommand.ArrayPool.Rent(colors.Length);
|
||||
colors.CopyTo(colorsCopy, 0);
|
||||
|
||||
_renderer.New<SetRenderTargetsCommand>()->Set(Ref(colorsCopy), Ref(depthStencil));
|
||||
_renderer.QueueCommand();
|
||||
}
|
||||
|
||||
|
||||
@@ -451,7 +451,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
|
||||
// TODO: Confirm behaviour on hardware.
|
||||
// When this is active, the origin appears to be on the bottom.
|
||||
if (_state.State.YControl.HasFlag(YControl.NegateY))
|
||||
if ((_state.State.YControl & YControl.NegateY) != 0)
|
||||
{
|
||||
dstY0 -= dstHeight;
|
||||
}
|
||||
|
||||
@@ -646,7 +646,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
int width = scissor.X2 - x;
|
||||
int height = scissor.Y2 - y;
|
||||
|
||||
if (_state.State.YControl.HasFlag(YControl.NegateY))
|
||||
if ((_state.State.YControl & YControl.NegateY) != 0)
|
||||
{
|
||||
ref ScreenScissorState screenScissor = ref _state.State.ScreenScissorState;
|
||||
y = screenScissor.Height - height - y;
|
||||
@@ -730,7 +730,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
FaceState face = _state.State.FaceState;
|
||||
|
||||
bool disableTransform = _state.State.ViewportTransformEnable == 0;
|
||||
bool yNegate = yControl.HasFlag(YControl.NegateY);
|
||||
bool yNegate = (yControl & YControl.NegateY) != 0;
|
||||
|
||||
UpdateFrontFace(yControl, face.FrontFace);
|
||||
UpdateDepthMode();
|
||||
@@ -1230,7 +1230,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
/// <param name="frontFace">Front face</param>
|
||||
private void UpdateFrontFace(YControl yControl, FrontFace frontFace)
|
||||
{
|
||||
bool isUpperLeftOrigin = !yControl.HasFlag(YControl.TriangleRastFlip);
|
||||
bool isUpperLeftOrigin = (yControl & YControl.TriangleRastFlip) == 0;
|
||||
|
||||
if (isUpperLeftOrigin)
|
||||
{
|
||||
@@ -1521,7 +1521,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
{
|
||||
// Make sure we update the viewport size on the support buffer if it will be consumed on the new shader.
|
||||
|
||||
if (!_fsReadsFragCoord && _state.State.YControl.HasFlag(YControl.NegateY))
|
||||
if (!_fsReadsFragCoord && (_state.State.YControl & YControl.NegateY) != 0)
|
||||
{
|
||||
UpdateSupportBufferViewportSize();
|
||||
}
|
||||
|
||||
@@ -381,9 +381,9 @@ namespace Ryujinx.Graphics.Gpu
|
||||
/// <param name="flags">Modifiers for how host sync should be created</param>
|
||||
internal void CreateHostSyncIfNeeded(HostSyncFlags flags)
|
||||
{
|
||||
bool syncpoint = flags.HasFlag(HostSyncFlags.Syncpoint);
|
||||
bool strict = flags.HasFlag(HostSyncFlags.Strict);
|
||||
bool force = flags.HasFlag(HostSyncFlags.Force);
|
||||
bool syncPoint = (flags & HostSyncFlags.Syncpoint) == HostSyncFlags.Syncpoint;
|
||||
bool strict = (flags & HostSyncFlags.Strict) == HostSyncFlags.Strict;
|
||||
bool force = (flags & HostSyncFlags.Force) == HostSyncFlags.Force;
|
||||
|
||||
if (BufferMigrations.Count > 0)
|
||||
{
|
||||
@@ -402,24 +402,37 @@ namespace Ryujinx.Graphics.Gpu
|
||||
}
|
||||
}
|
||||
|
||||
if (force || _pendingSync || (syncpoint && SyncpointActions.Count > 0))
|
||||
if (force || _pendingSync || (syncPoint && SyncpointActions.Count > 0))
|
||||
{
|
||||
foreach (ISyncActionHandler action in SyncActions)
|
||||
{
|
||||
action.SyncPreAction(syncpoint);
|
||||
action.SyncPreAction(syncPoint);
|
||||
}
|
||||
|
||||
foreach (ISyncActionHandler action in SyncpointActions)
|
||||
{
|
||||
action.SyncPreAction(syncpoint);
|
||||
action.SyncPreAction(syncPoint);
|
||||
}
|
||||
|
||||
Renderer.CreateSync(SyncNumber, strict);
|
||||
|
||||
SyncNumber++;
|
||||
|
||||
SyncActions.RemoveAll(action => action.SyncAction(syncpoint));
|
||||
SyncpointActions.RemoveAll(action => action.SyncAction(syncpoint));
|
||||
for (int i = 0; i < SyncActions.Count; i++)
|
||||
{
|
||||
if (SyncActions[i].SyncAction(syncPoint))
|
||||
{
|
||||
SyncActions.RemoveAt(i--);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < SyncpointActions.Count; i++)
|
||||
{
|
||||
if (SyncpointActions[i].SyncAction(syncPoint))
|
||||
{
|
||||
SyncpointActions.RemoveAt(i--);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_pendingSync = false;
|
||||
|
||||
@@ -1628,7 +1628,15 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
lock (_poolOwners)
|
||||
{
|
||||
int references = _poolOwners.RemoveAll(entry => entry.Pool == pool && entry.ID == id || id == -1);
|
||||
int references = 0;
|
||||
for (int i = 0; i < _poolOwners.Count; i++)
|
||||
{
|
||||
if (_poolOwners[i].Pool == pool && _poolOwners[i].ID == id || id == -1)
|
||||
{
|
||||
_poolOwners.RemoveAt(i--);
|
||||
references++;
|
||||
}
|
||||
}
|
||||
|
||||
if (references == 0)
|
||||
{
|
||||
|
||||
@@ -45,7 +45,11 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
/// </summary>
|
||||
private const int GranularLayerThreshold = 8;
|
||||
|
||||
private delegate void HandlesCallbackDelegate(int baseHandle, int regionCount, bool split = false);
|
||||
private delegate bool HandlesCallbackDelegate(int baseHandle, int regionCount, bool split = false, bool specialData = false);
|
||||
|
||||
private readonly HandlesCallbackDelegate _signalModifyingCallback;
|
||||
private readonly HandlesCallbackDelegate _discardDataCallback;
|
||||
private readonly HandlesCallbackDelegate _checkDirtyCallback;
|
||||
|
||||
/// <summary>
|
||||
/// The storage texture associated with this group.
|
||||
@@ -126,6 +130,10 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
|
||||
_incompatibleOverlaps = incompatibleOverlaps;
|
||||
_flushIncompatibleOverlaps = TextureCompatibility.IsFormatHostIncompatible(storage.Info, context.Capabilities);
|
||||
|
||||
_signalModifyingCallback = SignalModifyingCallback;
|
||||
_discardDataCallback = DiscardDataCallback;
|
||||
_checkDirtyCallback = CheckDirtyCallback;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -253,29 +261,33 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
/// <param name="consume">True to consume the dirty flags and reprotect, false to leave them as is</param>
|
||||
/// <returns>True if a flag was dirty, false otherwise</returns>
|
||||
public bool CheckDirty(Texture texture, bool consume)
|
||||
{
|
||||
EvaluateRelevantHandles(texture, _checkDirtyCallback, out bool dirty, consume);
|
||||
|
||||
return dirty;
|
||||
}
|
||||
|
||||
bool CheckDirtyCallback(int baseHandle, int regionCount, bool split, bool consume)
|
||||
{
|
||||
bool dirty = false;
|
||||
|
||||
EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) =>
|
||||
|
||||
for (int i = 0; i < regionCount; i++)
|
||||
{
|
||||
for (int i = 0; i < regionCount; i++)
|
||||
TextureGroupHandle group = _handles[baseHandle + i];
|
||||
|
||||
foreach (RegionHandle handle in group.Handles)
|
||||
{
|
||||
TextureGroupHandle group = _handles[baseHandle + i];
|
||||
|
||||
foreach (RegionHandle handle in group.Handles)
|
||||
if (handle.Dirty)
|
||||
{
|
||||
if (handle.Dirty)
|
||||
if (consume)
|
||||
{
|
||||
if (consume)
|
||||
{
|
||||
handle.Reprotect();
|
||||
}
|
||||
|
||||
dirty = true;
|
||||
handle.Reprotect();
|
||||
}
|
||||
|
||||
dirty = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return dirty;
|
||||
}
|
||||
@@ -287,15 +299,19 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
/// <param name="texture">The texture being discarded</param>
|
||||
public void DiscardData(Texture texture)
|
||||
{
|
||||
EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) =>
|
||||
EvaluateRelevantHandles(texture, _discardDataCallback, out _);
|
||||
}
|
||||
|
||||
bool DiscardDataCallback(int baseHandle, int regionCount, bool split, bool bound)
|
||||
{
|
||||
for (int i = 0; i < regionCount; i++)
|
||||
{
|
||||
for (int i = 0; i < regionCount; i++)
|
||||
{
|
||||
TextureGroupHandle group = _handles[baseHandle + i];
|
||||
TextureGroupHandle group = _handles[baseHandle + i];
|
||||
|
||||
group.DiscardData();
|
||||
}
|
||||
});
|
||||
group.DiscardData();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -307,7 +323,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
FlushIncompatibleOverlapsIfNeeded();
|
||||
|
||||
EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) =>
|
||||
EvaluateRelevantHandles(texture, (baseHandle, regionCount, split, bound) =>
|
||||
{
|
||||
bool dirty = false;
|
||||
bool anyModified = false;
|
||||
@@ -383,7 +399,9 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
texture.SynchronizeFull();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}, out _);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -460,7 +478,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
/// <param name="texture">The texture to synchronize dependents of</param>
|
||||
public void SynchronizeDependents(Texture texture)
|
||||
{
|
||||
EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) =>
|
||||
EvaluateRelevantHandles(texture, (baseHandle, regionCount, split, bound) =>
|
||||
{
|
||||
for (int i = 0; i < regionCount; i++)
|
||||
{
|
||||
@@ -468,7 +486,9 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
|
||||
group.SynchronizeDependents();
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}, out _);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -550,7 +570,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
tracked = tracked || ShouldFlushTriggerTracking();
|
||||
bool flushed = false;
|
||||
|
||||
EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) =>
|
||||
EvaluateRelevantHandles(texture, (baseHandle, regionCount, split, bound) =>
|
||||
{
|
||||
int startSlice = 0;
|
||||
int endSlice = 0;
|
||||
@@ -604,7 +624,9 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
|
||||
flushed = true;
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}, out _);
|
||||
|
||||
Storage.SignalModifiedDirty();
|
||||
|
||||
@@ -693,7 +715,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
|
||||
ClearIncompatibleOverlaps(texture);
|
||||
|
||||
EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) =>
|
||||
EvaluateRelevantHandles(texture, (baseHandle, regionCount, split, bound) =>
|
||||
{
|
||||
for (int i = 0; i < regionCount; i++)
|
||||
{
|
||||
@@ -701,7 +723,9 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
|
||||
group.SignalModified(_context);
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}, out _);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -714,16 +738,20 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
ModifiedSequence = _context.GetModifiedSequence();
|
||||
|
||||
ClearIncompatibleOverlaps(texture);
|
||||
|
||||
EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) =>
|
||||
|
||||
EvaluateRelevantHandles(texture, _signalModifyingCallback, out _, bound);
|
||||
}
|
||||
|
||||
bool SignalModifyingCallback(int baseHandle, int regionCount, bool split, bool bound)
|
||||
{
|
||||
for (int i = 0; i < regionCount; i++)
|
||||
{
|
||||
for (int i = 0; i < regionCount; i++)
|
||||
{
|
||||
TextureGroupHandle group = _handles[baseHandle + i];
|
||||
TextureGroupHandle group = _handles[baseHandle + i];
|
||||
|
||||
group.SignalModifying(bound, _context);
|
||||
}
|
||||
});
|
||||
group.SignalModifying(bound, _context);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -767,16 +795,16 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
/// A function to be called with the base index of the range of handles for the given texture, and the number of handles it covers.
|
||||
/// This can be called for multiple disjoint ranges, if required.
|
||||
/// </param>
|
||||
private void EvaluateRelevantHandles(Texture texture, HandlesCallbackDelegate callback)
|
||||
private void EvaluateRelevantHandles(Texture texture, HandlesCallbackDelegate callback, out bool result, bool specialData = false)
|
||||
{
|
||||
if (texture == Storage || !(_hasMipViews || _hasLayerViews))
|
||||
{
|
||||
callback(0, _handles.Length);
|
||||
result = callback(0, _handles.Length, specialData: specialData);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
EvaluateRelevantHandles(texture.FirstLayer, texture.FirstLevel, texture.Info.GetSlices(), texture.Info.Levels, callback);
|
||||
EvaluateRelevantHandles(texture.FirstLayer, texture.FirstLevel, texture.Info.GetSlices(), texture.Info.Levels, callback, out result, specialData);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -791,11 +819,13 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
/// A function to be called with the base index of the range of handles for the given texture, and the number of handles it covers.
|
||||
/// This can be called for multiple disjoint ranges, if required.
|
||||
/// </param>
|
||||
private void EvaluateRelevantHandles(int firstLayer, int firstLevel, int slices, int levels, HandlesCallbackDelegate callback)
|
||||
private void EvaluateRelevantHandles(int firstLayer, int firstLevel, int slices, int levels, HandlesCallbackDelegate callback, out bool result, bool specialData = false)
|
||||
{
|
||||
int targetLayerHandles = _hasLayerViews ? slices : 1;
|
||||
int targetLevelHandles = _hasMipViews ? levels : 1;
|
||||
|
||||
result = false;
|
||||
|
||||
if (_isBuffer)
|
||||
{
|
||||
return;
|
||||
@@ -808,7 +838,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
// When there are no layer views, the mips are at a consistent offset.
|
||||
|
||||
callback(firstLevel, targetLevelHandles);
|
||||
result = callback(firstLevel, targetLevelHandles, specialData: specialData);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -822,7 +852,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
|
||||
while (levels-- > 1)
|
||||
{
|
||||
callback(firstLayer + levelIndex, slices);
|
||||
result = callback(firstLayer + levelIndex, slices, specialData: specialData);
|
||||
|
||||
levelIndex += layerCount;
|
||||
layerCount = Math.Max(layerCount >> 1, 1);
|
||||
@@ -839,7 +869,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
totalSize += layerCount;
|
||||
}
|
||||
|
||||
callback(firstLayer + levelIndex, totalSize);
|
||||
result = callback(firstLayer + levelIndex, totalSize, specialData: specialData);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -856,12 +886,12 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
|
||||
for (int i = 0; i < slices; i++)
|
||||
{
|
||||
callback(firstLevel + (firstLayer + i) * levelHandles, targetLevelHandles, true);
|
||||
result = callback(firstLevel + (firstLayer + i) * levelHandles, targetLevelHandles, true, specialData: specialData);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
callback(firstLevel + firstLayer * levelHandles, targetLevelHandles + (targetLayerHandles - 1) * levelHandles);
|
||||
result = callback(firstLevel + firstLayer * levelHandles, targetLevelHandles + (targetLayerHandles - 1) * levelHandles, specialData: specialData);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1439,8 +1469,16 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
List<(int BaseHandle, int RegionCount)> targetRange = [];
|
||||
List<(int BaseHandle, int RegionCount)> otherRange = [];
|
||||
|
||||
EvaluateRelevantHandles(firstLayer, firstLevel, other.Info.GetSlices(), other.Info.Levels, (baseHandle, regionCount, split) => targetRange.Add((baseHandle, regionCount)));
|
||||
otherGroup.EvaluateRelevantHandles(other, (baseHandle, regionCount, split) => otherRange.Add((baseHandle, regionCount)));
|
||||
EvaluateRelevantHandles(firstLayer, firstLevel, other.Info.GetSlices(), other.Info.Levels, (baseHandle, regionCount, split, specialData) =>
|
||||
{
|
||||
targetRange.Add((baseHandle, regionCount));
|
||||
return true;
|
||||
}, out _);
|
||||
otherGroup.EvaluateRelevantHandles(other, (baseHandle, regionCount, split, specialData) =>
|
||||
{
|
||||
otherRange.Add((baseHandle, regionCount));
|
||||
return true;
|
||||
}, out _);
|
||||
|
||||
int targetIndex = 0;
|
||||
int otherIndex = 0;
|
||||
|
||||
@@ -93,6 +93,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
private ulong _dirtyStart = ulong.MaxValue;
|
||||
private ulong _dirtyEnd = ulong.MaxValue;
|
||||
|
||||
private readonly Action<ulong, ulong> _syncPreRangeAction;
|
||||
private readonly Action<ulong, ulong> _syncRangeAction;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the buffer.
|
||||
/// </summary>
|
||||
@@ -177,6 +180,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
_modifiedDelegate = RegionModified;
|
||||
|
||||
_virtualDependenciesLock = new ReaderWriterLockSlim();
|
||||
|
||||
_syncPreRangeAction = SyncPreRangeAction;
|
||||
_syncRangeAction = SyncRangeAction;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -401,13 +407,15 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
|
||||
if (_preFlush.ShouldCopy)
|
||||
{
|
||||
_modifiedRanges?.GetRangesAtSync(Address, Size, _context.SyncNumber, (address, size) =>
|
||||
{
|
||||
_preFlush.CopyModified(address, size);
|
||||
});
|
||||
_modifiedRanges?.GetRangesAtSync(Address, Size, _context.SyncNumber, _syncPreRangeAction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SyncPreRangeAction(ulong address, ulong size)
|
||||
{
|
||||
_preFlush.CopyModified(address, size);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Action to be performed when a syncpoint is reached after modification.
|
||||
@@ -420,11 +428,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
|
||||
if (_useGranular)
|
||||
{
|
||||
_modifiedRanges?.GetRanges(Address, Size, (address, size) =>
|
||||
{
|
||||
_memoryTrackingGranular.RegisterAction(address, size, _externalFlushDelegate);
|
||||
SynchronizeMemory(address, size);
|
||||
});
|
||||
|
||||
|
||||
_modifiedRanges?.GetRanges(Address, Size, _syncRangeAction);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -434,6 +440,12 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SyncRangeAction(ulong address, ulong size)
|
||||
{
|
||||
_memoryTrackingGranular.RegisterAction(address, size, _externalFlushDelegate);
|
||||
SynchronizeMemory(address, size);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inherit modified and dirty ranges from another buffer.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Ryujinx.Memory.Range;
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Linq;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Memory
|
||||
@@ -276,13 +277,18 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
{
|
||||
// We use the non-span method here because keeping the lock will cause a deadlock.
|
||||
Lock.EnterReadLock();
|
||||
RangeItem<BufferModifiedRange>[] overlaps = FindOverlapsAsArray(address, size);
|
||||
RangeItem<BufferModifiedRange>[] overlaps = FindOverlapsAsArray(address, size, out int length);
|
||||
Lock.ExitReadLock();
|
||||
|
||||
for (int i = 0; i < overlaps.Length; i++)
|
||||
if (length != 0)
|
||||
{
|
||||
BufferModifiedRange overlap = overlaps[i].Value;
|
||||
rangeAction(overlap.Address, overlap.Size);
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
BufferModifiedRange overlap = overlaps[i].Value;
|
||||
rangeAction(overlap.Address, overlap.Size);
|
||||
}
|
||||
|
||||
ArrayPool<RangeItem<BufferModifiedRange>>.Shared.Return(overlaps);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -392,9 +398,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
|
||||
Lock.EnterWriteLock();
|
||||
// We use the non-span method here because the array is partially modified by the code, which would invalidate a span.
|
||||
RangeItem<BufferModifiedRange>[] overlaps = FindOverlapsAsArray(address, size);
|
||||
|
||||
int rangeCount = overlaps.Length;
|
||||
RangeItem<BufferModifiedRange>[] overlaps = FindOverlapsAsArray(address, size, out int rangeCount);
|
||||
|
||||
if (rangeCount == 0)
|
||||
{
|
||||
@@ -410,7 +414,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
|
||||
for (int i = 0; i < rangeCount; i++)
|
||||
{
|
||||
BufferModifiedRange overlap = overlaps[i].Value;
|
||||
BufferModifiedRange overlap = overlaps![i].Value;
|
||||
|
||||
long diff = (long)(overlap.SyncNumber - currentSync);
|
||||
|
||||
@@ -430,7 +434,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
// Wait for the syncpoint.
|
||||
_context.Renderer.WaitSync(currentSync + (ulong)highestDiff);
|
||||
|
||||
RemoveRangesAndFlush(overlaps.ToArray(), rangeCount, highestDiff, currentSync, address, endAddress);
|
||||
RemoveRangesAndFlush(overlaps, rangeCount, highestDiff, currentSync, address, endAddress);
|
||||
|
||||
ArrayPool<RangeItem<BufferModifiedRange>>.Shared.Return(overlaps!);
|
||||
|
||||
Lock.ExitWriteLock();
|
||||
}
|
||||
|
||||
@@ -397,7 +397,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
/// <returns>True if queried, false otherwise</returns>
|
||||
public bool IsPrimitiveTopologyQueried()
|
||||
{
|
||||
return _queriedState.HasFlag(QueriedStateFlags.PrimitiveTopology);
|
||||
return (_queriedState & QueriedStateFlags.PrimitiveTopology) == QueriedStateFlags.PrimitiveTopology;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -904,7 +904,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
specState.PipelineState = pipelineState;
|
||||
}
|
||||
|
||||
if (specState._queriedState.HasFlag(QueriedStateFlags.TransformFeedback))
|
||||
if ((specState._queriedState & QueriedStateFlags.TransformFeedback) == QueriedStateFlags.TransformFeedback)
|
||||
{
|
||||
ushort tfCount = 0;
|
||||
dataReader.Read(ref tfCount);
|
||||
@@ -930,7 +930,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
specState._textureSpecialization[textureKey] = textureState;
|
||||
}
|
||||
|
||||
if (specState._queriedState.HasFlag(QueriedStateFlags.TextureArrayFromBuffer))
|
||||
if ((specState._queriedState & QueriedStateFlags.TextureArrayFromBuffer) == QueriedStateFlags.TextureArrayFromBuffer)
|
||||
{
|
||||
dataReader.Read(ref count);
|
||||
|
||||
@@ -946,7 +946,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
}
|
||||
}
|
||||
|
||||
if (specState._queriedState.HasFlag(QueriedStateFlags.TextureArrayFromPool))
|
||||
if ((specState._queriedState & QueriedStateFlags.TextureArrayFromPool) == QueriedStateFlags.TextureArrayFromPool)
|
||||
{
|
||||
dataReader.Read(ref count);
|
||||
|
||||
@@ -1006,7 +1006,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
dataWriter.WriteWithMagicAndSize(ref pipelineState, PgpsMagic);
|
||||
}
|
||||
|
||||
if (_queriedState.HasFlag(QueriedStateFlags.TransformFeedback))
|
||||
if ((_queriedState & QueriedStateFlags.TransformFeedback) == QueriedStateFlags.TransformFeedback)
|
||||
{
|
||||
ushort tfCount = (ushort)TransformFeedbackDescriptors.Length;
|
||||
dataWriter.Write(ref tfCount);
|
||||
@@ -1029,7 +1029,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
dataWriter.WriteWithMagicAndSize(ref textureState.Value, TexsMagic);
|
||||
}
|
||||
|
||||
if (_queriedState.HasFlag(QueriedStateFlags.TextureArrayFromBuffer))
|
||||
if ((_queriedState & QueriedStateFlags.TextureArrayFromBuffer) == QueriedStateFlags.TextureArrayFromBuffer)
|
||||
{
|
||||
count = (ushort)_textureArrayFromBufferSpecialization.Count;
|
||||
dataWriter.Write(ref count);
|
||||
@@ -1044,7 +1044,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
}
|
||||
}
|
||||
|
||||
if (_queriedState.HasFlag(QueriedStateFlags.TextureArrayFromPool))
|
||||
if ((_queriedState & QueriedStateFlags.TextureArrayFromPool) == QueriedStateFlags.TextureArrayFromPool)
|
||||
{
|
||||
count = (ushort)_textureArrayFromPoolSpecialization.Count;
|
||||
dataWriter.Write(ref count);
|
||||
|
||||
@@ -58,6 +58,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
private Dictionary<ulong, StagingBufferReserved> _mirrors;
|
||||
private bool _useMirrors;
|
||||
|
||||
private Action _decrementReferenceCount;
|
||||
|
||||
public BufferHolder(VulkanRenderer gd, Device device, VkBuffer buffer, MemoryAllocation allocation, int size, BufferAllocationType type, BufferAllocationType currentType)
|
||||
{
|
||||
_gd = gd;
|
||||
@@ -75,6 +77,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
_flushLock = new ReaderWriterLockSlim();
|
||||
_useMirrors = gd.IsTBDR;
|
||||
|
||||
_decrementReferenceCount = _buffer.DecrementReferenceCount;
|
||||
}
|
||||
|
||||
public BufferHolder(VulkanRenderer gd, Device device, VkBuffer buffer, Auto<MemoryAllocation> allocation, int size, BufferAllocationType type, BufferAllocationType currentType, int offset)
|
||||
@@ -444,7 +448,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
_flushLock.ExitReadLock();
|
||||
|
||||
return PinnedSpan<byte>.UnsafeFromSpan(result, _buffer.DecrementReferenceCount);
|
||||
return PinnedSpan<byte>.UnsafeFromSpan(result, _decrementReferenceCount);
|
||||
}
|
||||
|
||||
BackgroundResource resource = _gd.BackgroundResources.Get();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using Silk.NET.Vulkan;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Format = Ryujinx.Graphics.GAL.Format;
|
||||
using VkFormat = Silk.NET.Vulkan.Format;
|
||||
@@ -10,26 +11,27 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
class FramebufferParams
|
||||
{
|
||||
private readonly Device _device;
|
||||
private readonly Auto<DisposableImageView>[] _attachments;
|
||||
private readonly TextureView[] _colors;
|
||||
private readonly TextureView _depthStencil;
|
||||
private readonly TextureView[] _colorsCanonical;
|
||||
private readonly TextureView _baseAttachment;
|
||||
private readonly uint _validColorAttachments;
|
||||
private Auto<DisposableImageView>[] _attachments;
|
||||
private TextureView[] _colors;
|
||||
private TextureView _depthStencil;
|
||||
private TextureView[] _colorsCanonical;
|
||||
private TextureView _baseAttachment;
|
||||
private uint _validColorAttachments;
|
||||
private int _totalCount;
|
||||
|
||||
public uint Width { get; }
|
||||
public uint Height { get; }
|
||||
public uint Layers { get; }
|
||||
public uint Width { get; private set; }
|
||||
public uint Height { get; private set; }
|
||||
public uint Layers { get; private set; }
|
||||
|
||||
public uint[] AttachmentSamples { get; }
|
||||
public VkFormat[] AttachmentFormats { get; }
|
||||
public int[] AttachmentIndices { get; }
|
||||
public uint AttachmentIntegerFormatMask { get; }
|
||||
public bool LogicOpsAllowed { get; }
|
||||
public uint[] AttachmentSamples { get; private set; }
|
||||
public VkFormat[] AttachmentFormats { get; private set; }
|
||||
public int[] AttachmentIndices { get; private set; }
|
||||
public uint AttachmentIntegerFormatMask { get; private set; }
|
||||
public bool LogicOpsAllowed { get; private set; }
|
||||
|
||||
public int AttachmentsCount { get; }
|
||||
public int MaxColorAttachmentIndex => AttachmentIndices.Length > 0 ? AttachmentIndices[^1] : -1;
|
||||
public bool HasDepthStencil { get; }
|
||||
public int AttachmentsCount { get; private set; }
|
||||
public int MaxColorAttachmentIndex => ColorAttachmentsCount > 0 ? AttachmentIndices[ColorAttachmentsCount - 1] : -1;
|
||||
public bool HasDepthStencil { get; private set; }
|
||||
public int ColorAttachmentsCount => AttachmentsCount - (HasDepthStencil ? 1 : 0);
|
||||
|
||||
public FramebufferParams(Device device, TextureView view, uint width, uint height)
|
||||
@@ -50,7 +52,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
else
|
||||
{
|
||||
_colors = [view];
|
||||
_colorsCanonical = _colors;
|
||||
_colorsCanonical = [view];
|
||||
}
|
||||
|
||||
Width = width;
|
||||
@@ -64,6 +66,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
LogicOpsAllowed = !format.IsFloatOrSrgb();
|
||||
|
||||
AttachmentsCount = 1;
|
||||
_totalCount = 1;
|
||||
|
||||
HasDepthStencil = isDepthStencil;
|
||||
}
|
||||
@@ -133,7 +136,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
AttachmentIntegerFormatMask = attachmentIntegerFormatMask;
|
||||
LogicOpsAllowed = !allFormatsFloatOrSrgb;
|
||||
|
||||
if (depthStencil is TextureView dsTexture && dsTexture.Valid)
|
||||
if (depthStencil is TextureView { Valid: true } dsTexture)
|
||||
{
|
||||
_attachments[count - 1] = dsTexture.GetImageViewForAttachment();
|
||||
_depthStencil = dsTexture;
|
||||
@@ -159,11 +162,151 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
Layers = layers;
|
||||
|
||||
AttachmentsCount = count;
|
||||
_totalCount = colors.Length;
|
||||
}
|
||||
|
||||
public FramebufferParams Update(ITexture[] colors, ITexture depthStencil)
|
||||
{
|
||||
int colorsCount = colors.Count(IsValidTextureView);
|
||||
|
||||
int count = colorsCount + (IsValidTextureView(depthStencil) ? 1 : 0);
|
||||
|
||||
Array.Clear(_attachments);
|
||||
Array.Clear(_colors);
|
||||
|
||||
if (_attachments.Length < count)
|
||||
{
|
||||
Array.Resize(ref _attachments, count);
|
||||
}
|
||||
if (_colors.Length < colorsCount)
|
||||
{
|
||||
Array.Resize(ref _colors, colorsCount);
|
||||
}
|
||||
if (_colorsCanonical.Length < colors.Length)
|
||||
{
|
||||
Array.Resize(ref _colorsCanonical, colors.Length);
|
||||
}
|
||||
|
||||
for (int i = 0; i < colors.Length; i++)
|
||||
{
|
||||
ITexture color = colors[i];
|
||||
if (color is TextureView { Valid: true } view)
|
||||
{
|
||||
_colorsCanonical[i] = view;
|
||||
}
|
||||
else
|
||||
{
|
||||
_colorsCanonical[i] = null;
|
||||
}
|
||||
}
|
||||
|
||||
Array.Clear(AttachmentSamples);
|
||||
Array.Clear(AttachmentFormats);
|
||||
Array.Clear(AttachmentIndices);
|
||||
|
||||
if (AttachmentSamples.Length < count)
|
||||
{
|
||||
uint[] attachmentSamples = AttachmentSamples;
|
||||
Array.Resize(ref attachmentSamples, count);
|
||||
AttachmentSamples = attachmentSamples;
|
||||
}
|
||||
if (AttachmentFormats.Length < count)
|
||||
{
|
||||
VkFormat[] attachmentFormats = AttachmentFormats;
|
||||
Array.Resize(ref attachmentFormats, count);
|
||||
AttachmentFormats = attachmentFormats;
|
||||
}
|
||||
if (AttachmentIndices.Length < colorsCount)
|
||||
{
|
||||
int[] attachmentIndices = AttachmentIndices;
|
||||
Array.Resize(ref attachmentIndices, colorsCount);
|
||||
AttachmentIndices = attachmentIndices;
|
||||
}
|
||||
|
||||
uint width = uint.MaxValue;
|
||||
uint height = uint.MaxValue;
|
||||
uint layers = uint.MaxValue;
|
||||
|
||||
int index = 0;
|
||||
uint attachmentIntegerFormatMask = 0;
|
||||
bool allFormatsFloatOrSrgb = colorsCount != 0;
|
||||
|
||||
_validColorAttachments = 0;
|
||||
_baseAttachment = null;
|
||||
|
||||
for (int bindIndex = 0; bindIndex < colors.Length; bindIndex++)
|
||||
{
|
||||
TextureView texture = _colorsCanonical[bindIndex];
|
||||
if (texture is not null)
|
||||
{
|
||||
_attachments[index] = texture.GetImageViewForAttachment();
|
||||
_colors[index] = texture;
|
||||
_validColorAttachments |= 1u << bindIndex;
|
||||
_baseAttachment = texture;
|
||||
|
||||
AttachmentSamples[index] = (uint)texture.Info.Samples;
|
||||
AttachmentFormats[index] = texture.VkFormat;
|
||||
AttachmentIndices[index] = bindIndex;
|
||||
|
||||
Format format = texture.Info.Format;
|
||||
|
||||
if (format.IsInteger())
|
||||
{
|
||||
attachmentIntegerFormatMask |= 1u << bindIndex;
|
||||
}
|
||||
|
||||
allFormatsFloatOrSrgb &= format.IsFloatOrSrgb();
|
||||
|
||||
width = Math.Min(width, (uint)texture.Width);
|
||||
height = Math.Min(height, (uint)texture.Height);
|
||||
layers = Math.Min(layers, (uint)texture.Layers);
|
||||
|
||||
if (++index >= colorsCount)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AttachmentIntegerFormatMask = attachmentIntegerFormatMask;
|
||||
LogicOpsAllowed = !allFormatsFloatOrSrgb;
|
||||
_depthStencil = null;
|
||||
HasDepthStencil = false;
|
||||
|
||||
if (depthStencil is TextureView { Valid: true } dsTexture)
|
||||
{
|
||||
_attachments[count - 1] = dsTexture.GetImageViewForAttachment();
|
||||
_depthStencil = dsTexture;
|
||||
_baseAttachment ??= dsTexture;
|
||||
|
||||
AttachmentSamples[count - 1] = (uint)dsTexture.Info.Samples;
|
||||
AttachmentFormats[count - 1] = dsTexture.VkFormat;
|
||||
|
||||
width = Math.Min(width, (uint)dsTexture.Width);
|
||||
height = Math.Min(height, (uint)dsTexture.Height);
|
||||
layers = Math.Min(layers, (uint)dsTexture.Layers);
|
||||
|
||||
HasDepthStencil = true;
|
||||
}
|
||||
|
||||
if (count == 0)
|
||||
{
|
||||
width = height = layers = 1;
|
||||
}
|
||||
|
||||
Width = width;
|
||||
Height = height;
|
||||
Layers = layers;
|
||||
|
||||
AttachmentsCount = count;
|
||||
_totalCount = colors.Length;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Auto<DisposableImageView> GetAttachment(int index)
|
||||
{
|
||||
if ((uint)index >= _attachments.Length)
|
||||
if ((uint)index >= AttachmentsCount)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
@@ -183,7 +326,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
public ComponentType GetAttachmentComponentType(int index)
|
||||
{
|
||||
if (_colors != null && (uint)index < _colors.Length)
|
||||
if (_colors != null && (uint)index < ColorAttachmentsCount)
|
||||
{
|
||||
Format format = _colors[index].Info.Format;
|
||||
|
||||
@@ -218,7 +361,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
private static bool IsValidTextureView(ITexture texture)
|
||||
{
|
||||
return texture is TextureView view && view.Valid;
|
||||
return texture is TextureView { Valid: true };
|
||||
}
|
||||
|
||||
public ClearRect GetClearRect(Rectangle<int> scissor, int layer, int layerCount)
|
||||
@@ -233,9 +376,9 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
public unsafe Auto<DisposableFramebuffer> Create(Vk api, CommandBufferScoped cbs, Auto<DisposableRenderPass> renderPass)
|
||||
{
|
||||
ImageView* attachments = stackalloc ImageView[_attachments.Length];
|
||||
ImageView* attachments = stackalloc ImageView[AttachmentsCount];
|
||||
|
||||
for (int i = 0; i < _attachments.Length; i++)
|
||||
for (int i = 0; i < AttachmentsCount; i++)
|
||||
{
|
||||
attachments[i] = _attachments[i].Get(cbs).Value;
|
||||
}
|
||||
@@ -244,7 +387,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
SType = StructureType.FramebufferCreateInfo,
|
||||
RenderPass = renderPass.Get(cbs).Value,
|
||||
AttachmentCount = (uint)_attachments.Length,
|
||||
AttachmentCount = (uint)AttachmentsCount,
|
||||
PAttachments = attachments,
|
||||
Width = Width,
|
||||
Height = Height,
|
||||
@@ -252,14 +395,13 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
};
|
||||
|
||||
api.CreateFramebuffer(_device, in framebufferCreateInfo, null, out Framebuffer framebuffer).ThrowOnError();
|
||||
return new Auto<DisposableFramebuffer>(new DisposableFramebuffer(api, _device, framebuffer), null, _attachments);
|
||||
return new Auto<DisposableFramebuffer>(new DisposableFramebuffer(api, _device, framebuffer), null, _attachments[..AttachmentsCount]);
|
||||
}
|
||||
|
||||
public TextureView[] GetAttachmentViews()
|
||||
{
|
||||
TextureView[] result = new TextureView[_attachments.Length];
|
||||
|
||||
_colors?.CopyTo(result, 0);
|
||||
TextureView[] result = new TextureView[AttachmentsCount];
|
||||
_colors?.AsSpan(..ColorAttachmentsCount).CopyTo(result.AsSpan());
|
||||
|
||||
if (_depthStencil != null)
|
||||
{
|
||||
@@ -278,8 +420,11 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
if (_colors != null)
|
||||
{
|
||||
foreach (TextureView color in _colors)
|
||||
int count = ColorAttachmentsCount;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
TextureView color = _colors[i];
|
||||
// If Clear or DontCare were used, this would need to be write bit.
|
||||
color.Storage?.QueueLoadOpBarrier(cbs, false);
|
||||
}
|
||||
@@ -294,8 +439,11 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
if (_colors != null)
|
||||
{
|
||||
foreach (TextureView color in _colors)
|
||||
int count = ColorAttachmentsCount;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
TextureView color = _colors[i];
|
||||
color.Storage?.AddStoreOpUsage(false);
|
||||
}
|
||||
}
|
||||
@@ -307,7 +455,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
_depthStencil?.Storage.ClearBindings();
|
||||
|
||||
for (int i = 0; i < _colorsCanonical.Length; i++)
|
||||
for (int i = 0; i < _totalCount; i++)
|
||||
{
|
||||
_colorsCanonical[i]?.Storage.ClearBindings();
|
||||
}
|
||||
@@ -317,7 +465,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
_depthStencil?.Storage.AddBinding(_depthStencil);
|
||||
|
||||
for (int i = 0; i < _colorsCanonical.Length; i++)
|
||||
for (int i = 0; i < _totalCount; i++)
|
||||
{
|
||||
TextureView color = _colorsCanonical[i];
|
||||
color?.Storage.AddBinding(color);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Ryujinx.Common.Memory;
|
||||
using Silk.NET.Vulkan;
|
||||
using System;
|
||||
using System.Buffers;
|
||||
|
||||
namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
@@ -11,7 +12,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
private const int BufferUsageTrackingGranularity = 4096;
|
||||
|
||||
private readonly FenceHolder[] _fences;
|
||||
public FenceHolder[] Fences { get; }
|
||||
private readonly BufferUsageBitmap _bufferUsageBitmap;
|
||||
|
||||
/// <summary>
|
||||
@@ -19,7 +20,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
/// </summary>
|
||||
public MultiFenceHolder()
|
||||
{
|
||||
_fences = new FenceHolder[CommandBufferPool.MaxCommandBuffers];
|
||||
Fences = ArrayPool<FenceHolder>.Shared.Rent(CommandBufferPool.MaxCommandBuffers);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -28,7 +29,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
/// <param name="size">Size of the buffer</param>
|
||||
public MultiFenceHolder(int size)
|
||||
{
|
||||
_fences = new FenceHolder[CommandBufferPool.MaxCommandBuffers];
|
||||
Fences = ArrayPool<FenceHolder>.Shared.Rent(CommandBufferPool.MaxCommandBuffers);
|
||||
_bufferUsageBitmap = new BufferUsageBitmap(size, BufferUsageTrackingGranularity);
|
||||
}
|
||||
|
||||
@@ -90,7 +91,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
/// <returns>True if the command buffer's previous fence value was null</returns>
|
||||
public bool AddFence(int cbIndex, FenceHolder fence)
|
||||
{
|
||||
ref FenceHolder fenceRef = ref _fences[cbIndex];
|
||||
ref FenceHolder fenceRef = ref Fences[cbIndex];
|
||||
|
||||
if (fenceRef == null)
|
||||
{
|
||||
@@ -107,7 +108,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
/// <param name="cbIndex">Command buffer index of the command buffer that owns the fence</param>
|
||||
public void RemoveFence(int cbIndex)
|
||||
{
|
||||
_fences[cbIndex] = null;
|
||||
Fences[cbIndex] = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -117,7 +118,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
/// <returns>True if referenced, false otherwise</returns>
|
||||
public bool HasFence(int cbIndex)
|
||||
{
|
||||
return _fences[cbIndex] != null;
|
||||
return Fences[cbIndex] != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -227,9 +228,9 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
for (int i = 0; i < _fences.Length; i++)
|
||||
for (int i = 0; i < Fences.Length; i++)
|
||||
{
|
||||
FenceHolder fence = _fences[i];
|
||||
FenceHolder fence = Fences[i];
|
||||
|
||||
if (fence != null)
|
||||
{
|
||||
@@ -251,9 +252,9 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
for (int i = 0; i < _fences.Length; i++)
|
||||
for (int i = 0; i < Fences.Length; i++)
|
||||
{
|
||||
FenceHolder fence = _fences[i];
|
||||
FenceHolder fence = Fences[i];
|
||||
|
||||
if (fence != null && _bufferUsageBitmap.OverlapsWith(i, offset, size))
|
||||
{
|
||||
|
||||
@@ -1453,7 +1453,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
FramebufferParams?.ClearBindings();
|
||||
}
|
||||
|
||||
FramebufferParams = new FramebufferParams(Device, colors, depthStencil);
|
||||
FramebufferParams = FramebufferParams?.Update(colors, depthStencil) ?? new FramebufferParams(Device, colors, depthStencil);
|
||||
|
||||
if (IsMainPipeline)
|
||||
{
|
||||
@@ -1471,18 +1471,18 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
protected void UpdatePipelineAttachmentFormats()
|
||||
{
|
||||
Span<Silk.NET.Vulkan.Format> dstAttachmentFormats = _newState.Internal.AttachmentFormats.AsSpan();
|
||||
FramebufferParams.AttachmentFormats.CopyTo(dstAttachmentFormats);
|
||||
FramebufferParams.AttachmentFormats.AsSpan(..FramebufferParams.AttachmentsCount).CopyTo(dstAttachmentFormats);
|
||||
_newState.Internal.AttachmentIntegerFormatMask = FramebufferParams.AttachmentIntegerFormatMask;
|
||||
_newState.Internal.LogicOpsAllowed = FramebufferParams.LogicOpsAllowed;
|
||||
|
||||
for (int i = FramebufferParams.AttachmentFormats.Length; i < dstAttachmentFormats.Length; i++)
|
||||
for (int i = FramebufferParams.AttachmentsCount; i < dstAttachmentFormats.Length; i++)
|
||||
{
|
||||
dstAttachmentFormats[i] = 0;
|
||||
}
|
||||
|
||||
_newState.ColorBlendAttachmentStateCount = (uint)(FramebufferParams.MaxColorAttachmentIndex + 1);
|
||||
_newState.HasDepthStencil = FramebufferParams.HasDepthStencil;
|
||||
_newState.SamplesCount = FramebufferParams.AttachmentSamples.Length != 0 ? FramebufferParams.AttachmentSamples[0] : 1;
|
||||
_newState.SamplesCount = FramebufferParams.AttachmentsCount != 0 ? FramebufferParams.AttachmentSamples[0] : 1;
|
||||
}
|
||||
|
||||
protected unsafe void CreateRenderPass()
|
||||
|
||||
@@ -178,17 +178,16 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
if (_forcedFences.Count > 0)
|
||||
{
|
||||
_forcedFences.RemoveAll((entry) =>
|
||||
for (int i = 0; i < _forcedFences.Count; i++)
|
||||
{
|
||||
if (entry.Texture.Disposed)
|
||||
if (_forcedFences[i].Texture.Disposed)
|
||||
{
|
||||
return true;
|
||||
_forcedFences.RemoveAt(i--);
|
||||
continue;
|
||||
}
|
||||
|
||||
entry.Texture.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, entry.StageFlags);
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
_forcedFences[i].Texture.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, _forcedFences[i].StageFlags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Ryujinx.Common.Logging;
|
||||
using Silk.NET.Vulkan;
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
@@ -192,6 +193,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
_firstHandle = first.ID + 1;
|
||||
_handles.RemoveAt(0);
|
||||
ArrayPool<FenceHolder>.Shared.Return(first.Waitable.Fences);
|
||||
first.Waitable = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,11 +194,11 @@ namespace Ryujinx.HLE.FileSystem
|
||||
// TODO: Check Aoc version.
|
||||
if (!AocData.TryAdd(titleId, new AocItem(containerPath, ncaPath)))
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Application, $"Duplicate AddOnContent detected. TitleId {titleId:X16}");
|
||||
Logger.Warning?.Print(LogClass.Application, $"Duplicate AddOnContent detected. TitleId {titleId:X16} @ '{containerPath}'");
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Info?.Print(LogClass.Application, $"Found AddOnContent with TitleId {titleId:X16}");
|
||||
Logger.Notice.Print(LogClass.Application, $"Found AddOnContent with TitleId {titleId:X16} @ '{containerPath}'");
|
||||
|
||||
if (!mergedToContainer)
|
||||
{
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||
using System.Collections.Generic;
|
||||
|
||||
@@ -5,6 +6,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
|
||||
{
|
||||
class KSynchronizationObject : KAutoObject
|
||||
{
|
||||
private static readonly ObjectPool<LinkedListNode<KThread>> _nodePool = new(() => new LinkedListNode<KThread>(null));
|
||||
|
||||
public LinkedList<KThread> WaitingThreads { get; }
|
||||
|
||||
public KSynchronizationObject(KernelContext context) : base(context)
|
||||
@@ -14,12 +17,16 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
|
||||
|
||||
public LinkedListNode<KThread> AddWaitingThread(KThread thread)
|
||||
{
|
||||
return WaitingThreads.AddLast(thread);
|
||||
LinkedListNode<KThread> node = _nodePool.Allocate();
|
||||
node.Value = thread;
|
||||
WaitingThreads.AddLast(node);
|
||||
return node;
|
||||
}
|
||||
|
||||
public void RemoveWaitingThread(LinkedListNode<KThread> node)
|
||||
{
|
||||
WaitingThreads.Remove(node);
|
||||
_nodePool.Release(node);
|
||||
}
|
||||
|
||||
public virtual void Signal()
|
||||
|
||||
@@ -110,7 +110,22 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
{
|
||||
ulong size = PagesCount * KPageTableBase.PageSize;
|
||||
|
||||
return new KMemoryInfo(
|
||||
return KMemoryInfo.Pool.Allocate().Set(
|
||||
BaseAddress,
|
||||
size,
|
||||
State,
|
||||
Permission,
|
||||
Attribute,
|
||||
SourcePermission,
|
||||
IpcRefCount,
|
||||
DeviceRefCount);
|
||||
}
|
||||
|
||||
public KMemoryInfo GetInfo(KMemoryInfo oldInfo)
|
||||
{
|
||||
ulong size = PagesCount * KPageTableBase.PageSize;
|
||||
|
||||
return oldInfo.Set(
|
||||
BaseAddress,
|
||||
size,
|
||||
State,
|
||||
|
||||
@@ -1,19 +1,23 @@
|
||||
using Ryujinx.Common;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
{
|
||||
class KMemoryInfo
|
||||
{
|
||||
public ulong Address { get; }
|
||||
public ulong Size { get; }
|
||||
public static readonly ObjectPool<KMemoryInfo> Pool = new(() => new KMemoryInfo());
|
||||
|
||||
public ulong Address { get; private set; }
|
||||
public ulong Size { get; private set; }
|
||||
|
||||
public MemoryState State { get; }
|
||||
public KMemoryPermission Permission { get; }
|
||||
public MemoryAttribute Attribute { get; }
|
||||
public KMemoryPermission SourcePermission { get; }
|
||||
public MemoryState State { get; private set; }
|
||||
public KMemoryPermission Permission { get; private set; }
|
||||
public MemoryAttribute Attribute { get;private set; }
|
||||
public KMemoryPermission SourcePermission { get; private set; }
|
||||
|
||||
public int IpcRefCount { get; }
|
||||
public int DeviceRefCount { get; }
|
||||
public int IpcRefCount { get; private set; }
|
||||
public int DeviceRefCount { get; private set; }
|
||||
|
||||
public KMemoryInfo(
|
||||
public KMemoryInfo Set(
|
||||
ulong address,
|
||||
ulong size,
|
||||
MemoryState state,
|
||||
@@ -31,6 +35,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
SourcePermission = sourcePermission;
|
||||
IpcRefCount = ipcRefCount;
|
||||
DeviceRefCount = deviceRefCount;
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,17 +25,17 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
{
|
||||
MemoryPermission output = MemoryPermission.None;
|
||||
|
||||
if (permission.HasFlag(KMemoryPermission.Read))
|
||||
if ((permission & KMemoryPermission.Read) == KMemoryPermission.Read)
|
||||
{
|
||||
output = MemoryPermission.Read;
|
||||
}
|
||||
|
||||
if (permission.HasFlag(KMemoryPermission.Write))
|
||||
if ((permission & KMemoryPermission.Write) == KMemoryPermission.Write)
|
||||
{
|
||||
output |= MemoryPermission.Write;
|
||||
}
|
||||
|
||||
if (permission.HasFlag(KMemoryPermission.Execute))
|
||||
if ((permission & KMemoryPermission.Execute) == KMemoryPermission.Execute)
|
||||
{
|
||||
output |= MemoryPermission.Execute;
|
||||
}
|
||||
|
||||
@@ -998,7 +998,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
}
|
||||
else
|
||||
{
|
||||
return new KMemoryInfo(
|
||||
return KMemoryInfo.Pool.Allocate().Set(
|
||||
AddrSpaceEnd,
|
||||
~AddrSpaceEnd + 1,
|
||||
MemoryState.Reserved,
|
||||
@@ -2544,10 +2544,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
KMemoryPermission firstPermission = info.Permission;
|
||||
MemoryAttribute firstAttribute = info.Attribute;
|
||||
|
||||
do
|
||||
info = currBlock.GetInfo(info);
|
||||
|
||||
while (info.Address + info.Size - 1 < endAddr - 1 && (currBlock = currBlock.Successor) != null)
|
||||
{
|
||||
info = currBlock.GetInfo();
|
||||
|
||||
// Check if the block state matches what we expect.
|
||||
if (firstState != info.State ||
|
||||
firstPermission != info.Permission ||
|
||||
@@ -2559,11 +2559,16 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
outState = MemoryState.Unmapped;
|
||||
outPermission = KMemoryPermission.None;
|
||||
outAttribute = MemoryAttribute.None;
|
||||
|
||||
KMemoryInfo.Pool.Release(info);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
info = currBlock.GetInfo(info);
|
||||
}
|
||||
while (info.Address + info.Size - 1 < endAddr - 1 && (currBlock = currBlock.Successor) != null);
|
||||
|
||||
KMemoryInfo.Pool.Release(info);
|
||||
|
||||
outState = firstState;
|
||||
outPermission = firstPermission;
|
||||
@@ -2582,16 +2587,26 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
MemoryAttribute attributeMask,
|
||||
MemoryAttribute attributeExpected)
|
||||
{
|
||||
foreach (KMemoryInfo info in IterateOverRange(address, address + size))
|
||||
KMemoryBlock currBlock = _blockManager.FindBlock(address);
|
||||
|
||||
KMemoryInfo info = currBlock.GetInfo();
|
||||
|
||||
while (info.Address + info.Size - 1 < address + size - 1 && (currBlock = currBlock.Successor) != null)
|
||||
{
|
||||
// Check if the block state matches what we expect.
|
||||
if ((info.State & stateMask) != stateExpected ||
|
||||
(info.Permission & permissionMask) != permissionExpected ||
|
||||
(info.Attribute & attributeMask) != attributeExpected)
|
||||
{
|
||||
KMemoryInfo.Pool.Release(info);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
info = currBlock.GetInfo(info);
|
||||
}
|
||||
|
||||
KMemoryInfo.Pool.Release(info);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -2641,6 +2656,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
|
||||
ulong currBaseAddr = info.Address + reservedPagesCount * PageSize;
|
||||
ulong currEndAddr = info.Address + info.Size;
|
||||
|
||||
KMemoryInfo.Pool.Release(info);
|
||||
|
||||
if (aslrAddress >= regionStart &&
|
||||
aslrAddress >= currBaseAddr &&
|
||||
@@ -2721,6 +2738,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
allocationEndAddr <= regionEndAddr &&
|
||||
allocationEndAddr <= currEndAddr)
|
||||
{
|
||||
KMemoryInfo.Pool.Release(info);
|
||||
return address;
|
||||
}
|
||||
}
|
||||
@@ -2731,9 +2749,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
info = currBlock.GetInfo();
|
||||
|
||||
info = currBlock.GetInfo(info);
|
||||
}
|
||||
|
||||
KMemoryInfo.Pool.Release(info);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -386,6 +386,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||
}
|
||||
|
||||
ulong rwdataStart = roInfo.Address + roInfo.Size;
|
||||
|
||||
KMemoryInfo.Pool.Release(roInfo);
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||
using Ryujinx.Horizon.Common;
|
||||
@@ -11,6 +12,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||
class KAddressArbiter
|
||||
{
|
||||
private const int HasListenersMask = 0x40000000;
|
||||
private static readonly ObjectPool<KThread[]> _threadArrayPool = new(() => []);
|
||||
|
||||
private readonly KernelContext _context;
|
||||
|
||||
@@ -198,9 +200,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||
{
|
||||
_context.CriticalSection.Enter();
|
||||
|
||||
WakeThreads(_condVarThreads, count, TryAcquireMutex, x => x.CondVarAddress == address);
|
||||
static bool SignalProcessWideKeyPredicate(KThread thread, ulong address)
|
||||
{
|
||||
return thread.CondVarAddress == address;
|
||||
}
|
||||
|
||||
if (!_condVarThreads.Any(x => x.CondVarAddress == address))
|
||||
int validThreads = WakeThreads(_condVarThreads, count, TryAcquireMutex, SignalProcessWideKeyPredicate, address);
|
||||
|
||||
if (validThreads == 0)
|
||||
{
|
||||
KernelTransfer.KernelToUser(address, 0);
|
||||
}
|
||||
@@ -480,9 +487,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||
// or negative. It is incremented if there are no threads waiting.
|
||||
int waitingCount = 0;
|
||||
|
||||
foreach (KThread thread in _arbiterThreads.Where(x => x.MutexAddress == address))
|
||||
foreach (KThread thread in _arbiterThreads)
|
||||
{
|
||||
if (++waitingCount >= count)
|
||||
if (thread.MutexAddress == address &&
|
||||
++waitingCount >= count)
|
||||
{
|
||||
break;
|
||||
}
|
||||
@@ -553,23 +561,55 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||
thread.WaitingInArbitration = false;
|
||||
}
|
||||
|
||||
WakeThreads(_arbiterThreads, count, RemoveArbiterThread, x => x.MutexAddress == address);
|
||||
static bool ArbiterThreadPredecate(KThread thread, ulong address)
|
||||
{
|
||||
return thread.MutexAddress == address;
|
||||
}
|
||||
|
||||
WakeThreads(_arbiterThreads, count, RemoveArbiterThread, ArbiterThreadPredecate, address);
|
||||
}
|
||||
|
||||
private static void WakeThreads(
|
||||
private static int WakeThreads(
|
||||
List<KThread> threads,
|
||||
int count,
|
||||
Action<KThread> removeCallback,
|
||||
Func<KThread, bool> predicate)
|
||||
Func<KThread, ulong, bool> predicate,
|
||||
ulong address = 0)
|
||||
{
|
||||
IOrderedEnumerable<KThread> candidates = threads.Where(predicate).OrderBy(x => x.DynamicPriority);
|
||||
KThread[] toSignal = (count > 0 ? candidates.Take(count) : candidates).ToArray();
|
||||
KThread[] candidates = _threadArrayPool.Allocate();
|
||||
if (candidates.Length < threads.Count)
|
||||
{
|
||||
Array.Resize(ref candidates, threads.Count);
|
||||
}
|
||||
|
||||
int validCount = 0;
|
||||
|
||||
for (int i = 0; i < threads.Count; i++)
|
||||
{
|
||||
if (predicate(threads[i], address))
|
||||
{
|
||||
candidates[validCount++] = threads[i];
|
||||
}
|
||||
}
|
||||
|
||||
Span<KThread> candidatesSpan = candidates.AsSpan(..validCount);
|
||||
|
||||
candidatesSpan.Sort((x, y) => (x.DynamicPriority.CompareTo(y.DynamicPriority)));
|
||||
|
||||
foreach (KThread thread in toSignal)
|
||||
if (count > 0)
|
||||
{
|
||||
candidatesSpan = candidatesSpan[..Math.Min(count, candidatesSpan.Length)];
|
||||
}
|
||||
|
||||
foreach (KThread thread in candidatesSpan)
|
||||
{
|
||||
removeCallback(thread);
|
||||
threads.Remove(thread);
|
||||
}
|
||||
|
||||
_threadArrayPool.Release(candidates);
|
||||
|
||||
return validCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,8 +48,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||
|
||||
public KThreadContext ThreadContext { get; private set; }
|
||||
|
||||
public int DynamicPriority { get; set; }
|
||||
public ulong AffinityMask { get; set; }
|
||||
public int DynamicPriority { get; private set; }
|
||||
public ulong AffinityMask { get; private set; }
|
||||
|
||||
public ulong ThreadUid { get; private set; }
|
||||
|
||||
@@ -83,18 +83,18 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||
|
||||
public long LastScheduledTime { get; set; }
|
||||
|
||||
public LinkedListNode<KThread>[] SiblingsPerCore { get; private set; }
|
||||
public readonly LinkedListNode<KThread>[] SiblingsPerCore;
|
||||
|
||||
public LinkedList<KThread> Withholder { get; set; }
|
||||
public LinkedListNode<KThread> WithholderNode { get; set; }
|
||||
public readonly LinkedListNode<KThread> WithholderNode;
|
||||
|
||||
public LinkedListNode<KThread> ProcessListNode { get; set; }
|
||||
public readonly LinkedListNode<KThread> ProcessListNode;
|
||||
|
||||
private readonly LinkedList<KThread> _mutexWaiters;
|
||||
private LinkedListNode<KThread> _mutexWaiterNode;
|
||||
private readonly LinkedListNode<KThread> _mutexWaiterNode;
|
||||
|
||||
private readonly LinkedList<KThread> _pinnedWaiters;
|
||||
private LinkedListNode<KThread> _pinnedWaiterNode;
|
||||
private readonly LinkedListNode<KThread> _pinnedWaiterNode;
|
||||
|
||||
public KThread MutexOwner { get; private set; }
|
||||
|
||||
@@ -1070,11 +1070,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||
|
||||
if (nextPrio != null)
|
||||
{
|
||||
thread._mutexWaiterNode = _mutexWaiters.AddBefore(nextPrio, thread);
|
||||
_mutexWaiters.AddBefore(nextPrio, thread._mutexWaiterNode);
|
||||
}
|
||||
else
|
||||
{
|
||||
thread._mutexWaiterNode = _mutexWaiters.AddLast(thread);
|
||||
_mutexWaiters.AddLast(thread._mutexWaiterNode);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
using LibHac;
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Sf;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Configuration;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Memory;
|
||||
using System.Threading;
|
||||
|
||||
@@ -40,7 +42,19 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
|
||||
}
|
||||
|
||||
using WritableRegion region = context.Memory.GetWritableRegion(bufferAddress, (int)bufferLen, true);
|
||||
Result result = _baseStorage.Get.Read((long)offset, new OutBuffer(region.Memory.Span), (long)size);
|
||||
Result result;
|
||||
|
||||
try
|
||||
{
|
||||
result = _baseStorage.Get.Read((long)offset, new OutBuffer(region.Memory.Span), (long)size);
|
||||
}
|
||||
catch (HorizonResultException hre) when (hre.IsOfResultType(ResultFs.NonRealDataVerificationFailed))
|
||||
{
|
||||
Logger.Error?.Print(LogClass.ServiceFs,
|
||||
$"Encountered corrupted data in filesystem storage @ offset 0x{offset:X8}, size 0x{size:X8}. " +
|
||||
"Please redump the current game and/or update from your console.");
|
||||
result = ResultFs.NonRealDataVerificationFailed;
|
||||
}
|
||||
|
||||
if (context.Device.DirtyHacks.IsEnabled(DirtyHack.Xc2MenuSoftlockFix) && IsXc2)
|
||||
{
|
||||
|
||||
@@ -14,6 +14,7 @@ using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap;
|
||||
using Ryujinx.HLE.HOS.Services.Nv.Types;
|
||||
using Ryujinx.Memory;
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
|
||||
@@ -46,6 +47,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv
|
||||
{ "/dev/nvhost-dbg-gpu", typeof(NvHostDbgGpuDeviceFile) },
|
||||
{ "/dev/nvhost-prof-gpu", typeof(NvHostProfGpuDeviceFile) },
|
||||
};
|
||||
|
||||
private static readonly ArrayPool<byte> _byteArrayPool = ArrayPool<byte>.Create();
|
||||
|
||||
public static IdDictionary DeviceFileIdRegistry = new();
|
||||
|
||||
@@ -471,10 +474,13 @@ namespace Ryujinx.HLE.HOS.Services.Nv
|
||||
|
||||
errorCode = GetIoctlArgument(context, ioctlCommand, out Span<byte> arguments);
|
||||
|
||||
if (!context.Memory.TryReadUnsafe(inlineInBufferPosition, (int)inlineInBufferSize, out Span<byte> inlineInBuffer))
|
||||
byte[] inlineInBuffer = null;
|
||||
|
||||
if (!context.Memory.TryReadUnsafe(inlineInBufferPosition, (int)inlineInBufferSize, out Span<byte> inlineInBufferSpan))
|
||||
{
|
||||
inlineInBuffer = new byte[inlineInBufferSize];
|
||||
context.Memory.Read(inlineInBufferPosition, inlineInBuffer);
|
||||
inlineInBuffer = _byteArrayPool.Rent((int)inlineInBufferSize);
|
||||
inlineInBufferSpan = inlineInBuffer;
|
||||
context.Memory.Read(inlineInBufferPosition, inlineInBufferSpan[..(int)inlineInBufferSize]);
|
||||
}
|
||||
|
||||
if (errorCode == NvResult.Success)
|
||||
@@ -483,7 +489,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv
|
||||
|
||||
if (errorCode == NvResult.Success)
|
||||
{
|
||||
NvInternalResult internalResult = deviceFile.Ioctl2(ioctlCommand, arguments, inlineInBuffer);
|
||||
NvInternalResult internalResult = deviceFile.Ioctl2(ioctlCommand, arguments, inlineInBufferSpan[..(int)inlineInBufferSize]);
|
||||
|
||||
if (internalResult == NvInternalResult.NotImplemented)
|
||||
{
|
||||
@@ -498,6 +504,11 @@ namespace Ryujinx.HLE.HOS.Services.Nv
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (inlineInBuffer is not null)
|
||||
{
|
||||
_byteArrayPool.Return(inlineInBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
context.ResponseData.Write((uint)errorCode);
|
||||
@@ -520,10 +531,13 @@ namespace Ryujinx.HLE.HOS.Services.Nv
|
||||
|
||||
errorCode = GetIoctlArgument(context, ioctlCommand, out Span<byte> arguments);
|
||||
|
||||
if (!context.Memory.TryReadUnsafe(inlineOutBufferPosition, (int)inlineOutBufferSize, out Span<byte> inlineOutBuffer))
|
||||
byte[] inlineOutBuffer = null;
|
||||
|
||||
if (!context.Memory.TryReadUnsafe(inlineOutBufferPosition, (int)inlineOutBufferSize, out Span<byte> inlineOutBufferSpan))
|
||||
{
|
||||
inlineOutBuffer = new byte[inlineOutBufferSize];
|
||||
context.Memory.Read(inlineOutBufferPosition, inlineOutBuffer);
|
||||
inlineOutBuffer = _byteArrayPool.Rent((int)inlineOutBufferSize);
|
||||
inlineOutBufferSpan = inlineOutBuffer;
|
||||
context.Memory.Read(inlineOutBufferPosition, inlineOutBufferSpan[..(int)inlineOutBufferSize]);
|
||||
}
|
||||
|
||||
if (errorCode == NvResult.Success)
|
||||
@@ -532,7 +546,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv
|
||||
|
||||
if (errorCode == NvResult.Success)
|
||||
{
|
||||
NvInternalResult internalResult = deviceFile.Ioctl3(ioctlCommand, arguments, inlineOutBuffer);
|
||||
NvInternalResult internalResult = deviceFile.Ioctl3(ioctlCommand, arguments, inlineOutBufferSpan[..(int)inlineOutBufferSize]);
|
||||
|
||||
if (internalResult == NvInternalResult.NotImplemented)
|
||||
{
|
||||
@@ -544,10 +558,15 @@ namespace Ryujinx.HLE.HOS.Services.Nv
|
||||
if ((ioctlCommand.DirectionValue & NvIoctl.Direction.Write) != 0)
|
||||
{
|
||||
context.Memory.Write(context.Request.GetBufferType0x22(0).Position, arguments.ToArray());
|
||||
context.Memory.Write(inlineOutBufferPosition, inlineOutBuffer.ToArray());
|
||||
context.Memory.Write(inlineOutBufferPosition, inlineOutBufferSpan[..(int)inlineOutBufferSize].ToArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (inlineOutBuffer is not null)
|
||||
{
|
||||
_byteArrayPool.Return(inlineOutBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
context.ResponseData.Write((uint)errorCode);
|
||||
|
||||
@@ -7,7 +7,7 @@ using System.Threading;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
|
||||
{
|
||||
class BsdContext
|
||||
class BsdContext : IDisposable
|
||||
{
|
||||
private static readonly ConcurrentDictionary<ulong, BsdContext> _registry = new();
|
||||
|
||||
@@ -158,6 +158,20 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
|
||||
return LinuxError.SUCCESS;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
int count;
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
count = _fds.Count;
|
||||
}
|
||||
|
||||
for (int fd = 0; fd < count; fd++) {
|
||||
CloseFileDescriptor(fd);
|
||||
}
|
||||
}
|
||||
|
||||
public static BsdContext GetOrRegister(ulong processId)
|
||||
{
|
||||
BsdContext context = GetContext(processId);
|
||||
@@ -174,12 +188,15 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
|
||||
|
||||
public static BsdContext GetContext(ulong processId)
|
||||
{
|
||||
if (!_registry.TryGetValue(processId, out BsdContext processContext))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return _registry.GetValueOrDefault(processId);
|
||||
}
|
||||
|
||||
return processContext;
|
||||
public static void DeleteContext(ulong processId)
|
||||
{
|
||||
if (_registry.Remove(processId, out BsdContext context))
|
||||
{
|
||||
context.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1165,5 +1165,13 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
|
||||
|
||||
return WriteBsdResult(context, newSockFd, errno);
|
||||
}
|
||||
|
||||
|
||||
public override void DestroyAtExit()
|
||||
{
|
||||
if (_context != null) {
|
||||
_context.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,11 +102,13 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
|
||||
return (false, ProcessResult.Failed);
|
||||
}
|
||||
|
||||
(Nca updatePatchNca, Nca updateControlNca) = mainNca.GetUpdateData(device.FileSystem, device.System.FsIntegrityCheckLevel, device.Configuration.UserChannelPersistence.Index, out string _);
|
||||
(Nca updatePatchNca, Nca updateControlNca) = mainNca.GetUpdateData(device.FileSystem, device.System.FsIntegrityCheckLevel, device.Configuration.UserChannelPersistence.Index, out string updatePath);
|
||||
|
||||
if (updatePatchNca != null)
|
||||
{
|
||||
patchNca = updatePatchNca;
|
||||
if (updatePath != null)
|
||||
Logger.Notice.PrintMsg(LogClass.Application, $"Loading update NCA from '{updatePath}'.");
|
||||
}
|
||||
|
||||
if (updateControlNca != null)
|
||||
|
||||
@@ -48,36 +48,36 @@ namespace Ryujinx.Horizon.Sdk.Sf
|
||||
case CommandArgType.Buffer:
|
||||
HipcBufferFlags flags = argInfo.BufferFlags;
|
||||
|
||||
if (flags.HasFlag(HipcBufferFlags.In))
|
||||
if ((flags & HipcBufferFlags.In) != 0)
|
||||
{
|
||||
if (flags.HasFlag(HipcBufferFlags.AutoSelect))
|
||||
if ((flags & HipcBufferFlags.AutoSelect) != 0)
|
||||
{
|
||||
_inMapAliasBuffersCount++;
|
||||
_inPointerBuffersCount++;
|
||||
}
|
||||
else if (flags.HasFlag(HipcBufferFlags.MapAlias))
|
||||
else if ((flags & HipcBufferFlags.MapAlias) != 0)
|
||||
{
|
||||
_inMapAliasBuffersCount++;
|
||||
}
|
||||
else if (flags.HasFlag(HipcBufferFlags.Pointer))
|
||||
else if ((flags & HipcBufferFlags.Pointer) != 0)
|
||||
{
|
||||
_inPointerBuffersCount++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bool autoSelect = flags.HasFlag(HipcBufferFlags.AutoSelect);
|
||||
if (autoSelect || flags.HasFlag(HipcBufferFlags.Pointer))
|
||||
bool autoSelect = (flags & HipcBufferFlags.AutoSelect) != 0;
|
||||
if (autoSelect || (flags & HipcBufferFlags.Pointer) != 0)
|
||||
{
|
||||
_outPointerBuffersCount++;
|
||||
|
||||
if (flags.HasFlag(HipcBufferFlags.FixedSize))
|
||||
if ((flags & HipcBufferFlags.FixedSize) != 0)
|
||||
{
|
||||
_outFixedSizePointerBuffersCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (autoSelect || flags.HasFlag(HipcBufferFlags.MapAlias))
|
||||
if (autoSelect || (flags & HipcBufferFlags.MapAlias) != 0)
|
||||
{
|
||||
_outMapAliasBuffersCount++;
|
||||
}
|
||||
@@ -150,17 +150,17 @@ namespace Ryujinx.Horizon.Sdk.Sf
|
||||
HipcBufferFlags flags = _args[i].BufferFlags;
|
||||
bool isMapAlias;
|
||||
|
||||
if (flags.HasFlag(HipcBufferFlags.MapAlias))
|
||||
if ((flags & HipcBufferFlags.MapAlias) != 0)
|
||||
{
|
||||
isMapAlias = true;
|
||||
}
|
||||
else if (flags.HasFlag(HipcBufferFlags.Pointer))
|
||||
else if ((flags & HipcBufferFlags.Pointer) != 0)
|
||||
{
|
||||
isMapAlias = false;
|
||||
}
|
||||
else /* if (flags.HasFlag(HipcBufferFlags.HipcAutoSelect)) */
|
||||
else /* if (flags & HipcBufferFlags.HipcAutoSelect)) */
|
||||
{
|
||||
HipcBufferDescriptor descriptor = flags.HasFlag(HipcBufferFlags.In)
|
||||
HipcBufferDescriptor descriptor = (flags & HipcBufferFlags.In) != 0
|
||||
? context.Request.Data.SendBuffers[sendMapAliasIndex]
|
||||
: context.Request.Data.ReceiveBuffers[recvMapAliasIndex];
|
||||
|
||||
@@ -171,7 +171,7 @@ namespace Ryujinx.Horizon.Sdk.Sf
|
||||
|
||||
if (isMapAlias)
|
||||
{
|
||||
HipcBufferDescriptor descriptor = flags.HasFlag(HipcBufferFlags.In)
|
||||
HipcBufferDescriptor descriptor = (flags & HipcBufferFlags.In) != 0
|
||||
? context.Request.Data.SendBuffers[sendMapAliasIndex++]
|
||||
: context.Request.Data.ReceiveBuffers[recvMapAliasIndex++];
|
||||
|
||||
@@ -184,7 +184,7 @@ namespace Ryujinx.Horizon.Sdk.Sf
|
||||
}
|
||||
else
|
||||
{
|
||||
if (flags.HasFlag(HipcBufferFlags.In))
|
||||
if ((flags & HipcBufferFlags.In) != 0)
|
||||
{
|
||||
HipcStaticDescriptor descriptor = context.Request.Data.SendStatics[sendPointerIndex++];
|
||||
ulong address = descriptor.Address;
|
||||
@@ -197,11 +197,11 @@ namespace Ryujinx.Horizon.Sdk.Sf
|
||||
pointerBufferTail = Math.Max(pointerBufferTail, address + size);
|
||||
}
|
||||
}
|
||||
else /* if (flags.HasFlag(HipcBufferFlags.Out)) */
|
||||
else /* if (flags & HipcBufferFlags.Out)) */
|
||||
{
|
||||
ulong size;
|
||||
|
||||
if (flags.HasFlag(HipcBufferFlags.FixedSize))
|
||||
if ((flags & HipcBufferFlags.FixedSize) != 0)
|
||||
{
|
||||
size = _args[i].BufferFixedSize;
|
||||
}
|
||||
@@ -234,12 +234,12 @@ namespace Ryujinx.Horizon.Sdk.Sf
|
||||
|
||||
private static bool IsMapTransferModeValid(HipcBufferFlags flags, HipcBufferMode mode)
|
||||
{
|
||||
if (flags.HasFlag(HipcBufferFlags.MapTransferAllowsNonSecure))
|
||||
if ((flags & HipcBufferFlags.MapTransferAllowsNonSecure) != 0)
|
||||
{
|
||||
return mode == HipcBufferMode.NonSecure;
|
||||
}
|
||||
|
||||
if (flags.HasFlag(HipcBufferFlags.MapTransferAllowsNonDevice))
|
||||
if ((flags & HipcBufferFlags.MapTransferAllowsNonDevice) != 0)
|
||||
{
|
||||
return mode == HipcBufferMode.NonDevice;
|
||||
}
|
||||
@@ -259,18 +259,18 @@ namespace Ryujinx.Horizon.Sdk.Sf
|
||||
}
|
||||
|
||||
HipcBufferFlags flags = _args[i].BufferFlags;
|
||||
if (!flags.HasFlag(HipcBufferFlags.Out))
|
||||
if ((flags & HipcBufferFlags.Out) == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
PointerAndSize buffer = _bufferRanges[i];
|
||||
|
||||
if (flags.HasFlag(HipcBufferFlags.Pointer))
|
||||
if ((flags & HipcBufferFlags.Pointer) != 0)
|
||||
{
|
||||
response.SendStatics[recvPointerIndex] = new HipcStaticDescriptor(buffer.Address, (ushort)buffer.Size, recvPointerIndex);
|
||||
}
|
||||
else if (flags.HasFlag(HipcBufferFlags.AutoSelect))
|
||||
else if ((flags & HipcBufferFlags.AutoSelect) != 0)
|
||||
{
|
||||
if (!isBufferMapAlias[i])
|
||||
{
|
||||
|
||||
@@ -1,236 +0,0 @@
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.SDL2.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using static SDL2.SDL;
|
||||
|
||||
namespace Ryujinx.Input.SDL2
|
||||
{
|
||||
public class SDL2GamepadDriver : IGamepadDriver
|
||||
{
|
||||
private readonly Dictionary<int, string> _gamepadsInstanceIdsMapping;
|
||||
private readonly List<string> _gamepadsIds;
|
||||
private readonly Lock _lock = new();
|
||||
|
||||
public ReadOnlySpan<string> GamepadsIds
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return _gamepadsIds.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string DriverName => "SDL2";
|
||||
|
||||
public event Action<string> OnGamepadConnected;
|
||||
public event Action<string> OnGamepadDisconnected;
|
||||
|
||||
public SDL2GamepadDriver()
|
||||
{
|
||||
_gamepadsInstanceIdsMapping = new Dictionary<int, string>();
|
||||
_gamepadsIds = [];
|
||||
|
||||
SDL2Driver.Instance.Initialize();
|
||||
SDL2Driver.Instance.OnJoyStickConnected += HandleJoyStickConnected;
|
||||
SDL2Driver.Instance.OnJoystickDisconnected += HandleJoyStickDisconnected;
|
||||
SDL2Driver.Instance.OnJoyBatteryUpdated += HandleJoyBatteryUpdated;
|
||||
|
||||
// Add already connected gamepads
|
||||
int numJoysticks = SDL_NumJoysticks();
|
||||
|
||||
for (int joystickIndex = 0; joystickIndex < numJoysticks; joystickIndex++)
|
||||
{
|
||||
HandleJoyStickConnected(joystickIndex, SDL_JoystickGetDeviceInstanceID(joystickIndex));
|
||||
}
|
||||
}
|
||||
|
||||
private string GenerateGamepadId(int joystickIndex)
|
||||
{
|
||||
Guid guid = SDL_JoystickGetDeviceGUID(joystickIndex);
|
||||
|
||||
// Add a unique identifier to the start of the GUID in case of duplicates.
|
||||
|
||||
if (guid == Guid.Empty)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Remove the first 4 char of the guid (CRC part) to make it stable
|
||||
string guidString = $"0000{guid.ToString()[4..]}";
|
||||
|
||||
string id;
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
int guidIndex = 0;
|
||||
id = guidIndex + "-" + guidString;
|
||||
|
||||
while (_gamepadsIds.Contains(id))
|
||||
{
|
||||
id = (++guidIndex) + "-" + guidString;
|
||||
}
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
private int GetJoystickIndexByGamepadId(string id)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return _gamepadsIds.IndexOf(id);
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleJoyStickDisconnected(int joystickInstanceId)
|
||||
{
|
||||
bool joyConPairDisconnected = false;
|
||||
|
||||
if (!_gamepadsInstanceIdsMapping.Remove(joystickInstanceId, out string id))
|
||||
return;
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
_gamepadsIds.Remove(id);
|
||||
if (!SDL2JoyConPair.IsCombinable(_gamepadsIds))
|
||||
{
|
||||
_gamepadsIds.Remove(SDL2JoyConPair.Id);
|
||||
joyConPairDisconnected = true;
|
||||
}
|
||||
}
|
||||
|
||||
OnGamepadDisconnected?.Invoke(id);
|
||||
if (joyConPairDisconnected)
|
||||
{
|
||||
OnGamepadDisconnected?.Invoke(SDL2JoyConPair.Id);
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleJoyStickConnected(int joystickDeviceId, int joystickInstanceId)
|
||||
{
|
||||
bool joyConPairConnected = false;
|
||||
|
||||
if (SDL_IsGameController(joystickDeviceId) == SDL_bool.SDL_TRUE)
|
||||
{
|
||||
if (_gamepadsInstanceIdsMapping.ContainsKey(joystickInstanceId))
|
||||
{
|
||||
// Sometimes a JoyStick connected event fires after the app starts even though it was connected before
|
||||
// so it is rejected to avoid doubling the entries.
|
||||
return;
|
||||
}
|
||||
|
||||
string id = GenerateGamepadId(joystickDeviceId);
|
||||
|
||||
if (id == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_gamepadsInstanceIdsMapping.TryAdd(joystickInstanceId, id))
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (joystickDeviceId <= _gamepadsIds.FindLastIndex(_ => true))
|
||||
_gamepadsIds.Insert(joystickDeviceId, id);
|
||||
else
|
||||
_gamepadsIds.Add(id);
|
||||
|
||||
if (SDL2JoyConPair.IsCombinable(_gamepadsIds))
|
||||
{
|
||||
_gamepadsIds.Remove(SDL2JoyConPair.Id);
|
||||
_gamepadsIds.Add(SDL2JoyConPair.Id);
|
||||
joyConPairConnected = true;
|
||||
}
|
||||
}
|
||||
|
||||
OnGamepadConnected?.Invoke(id);
|
||||
if (joyConPairConnected)
|
||||
{
|
||||
OnGamepadConnected?.Invoke(SDL2JoyConPair.Id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleJoyBatteryUpdated(int joystickDeviceId, SDL_JoystickPowerLevel powerLevel)
|
||||
{
|
||||
Logger.Info?.Print(LogClass.Hid,
|
||||
$"{SDL_GameControllerNameForIndex(joystickDeviceId)} power level: {powerLevel}");
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
SDL2Driver.Instance.OnJoyStickConnected -= HandleJoyStickConnected;
|
||||
SDL2Driver.Instance.OnJoystickDisconnected -= HandleJoyStickDisconnected;
|
||||
|
||||
// Simulate a full disconnect when disposing
|
||||
foreach (string id in _gamepadsIds)
|
||||
{
|
||||
OnGamepadDisconnected?.Invoke(id);
|
||||
}
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
_gamepadsIds.Clear();
|
||||
}
|
||||
|
||||
SDL2Driver.Instance.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
GC.SuppressFinalize(this);
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
public IGamepad GetGamepad(string id)
|
||||
{
|
||||
if (id == SDL2JoyConPair.Id)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return SDL2JoyConPair.GetGamepad(_gamepadsIds);
|
||||
}
|
||||
}
|
||||
|
||||
int joystickIndex = GetJoystickIndexByGamepadId(id);
|
||||
|
||||
if (joystickIndex == -1)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
nint gamepadHandle = SDL_GameControllerOpen(joystickIndex);
|
||||
|
||||
if (gamepadHandle == nint.Zero)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (SDL_GameControllerName(gamepadHandle).StartsWith(SDL2JoyCon.Prefix))
|
||||
{
|
||||
return new SDL2JoyCon(gamepadHandle, id);
|
||||
}
|
||||
|
||||
return new SDL2Gamepad(gamepadHandle, id);
|
||||
}
|
||||
|
||||
public IEnumerable<IGamepad> GetGamepads()
|
||||
{
|
||||
lock (_gamepadsIds)
|
||||
{
|
||||
foreach (string gamepadId in _gamepadsIds)
|
||||
{
|
||||
yield return GetGamepad(gamepadId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ryujinx.Input\Ryujinx.Input.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.SDL2.Common\Ryujinx.SDL2.Common.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.SDL3.Common\Ryujinx.SDL3.Common.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -6,11 +6,12 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using System.Threading;
|
||||
using static SDL2.SDL;
|
||||
using SDL;
|
||||
using static SDL.SDL3;
|
||||
|
||||
namespace Ryujinx.Input.SDL2
|
||||
namespace Ryujinx.Input.SDL3
|
||||
{
|
||||
public class SDL2Gamepad : IGamepad
|
||||
public unsafe class SDL3Gamepad : IGamepad
|
||||
{
|
||||
private bool HasConfiguration => _configuration != null;
|
||||
|
||||
@@ -21,43 +22,43 @@ namespace Ryujinx.Input.SDL2
|
||||
|
||||
private StandardControllerInputConfig _configuration;
|
||||
|
||||
private static readonly SDL_GameControllerButton[] _buttonsDriverMapping =
|
||||
private static readonly SDL_GamepadButton[] _buttonsDriverMapping =
|
||||
[
|
||||
// Unbound, ignored.
|
||||
SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_INVALID,
|
||||
SDL_GamepadButton.SDL_GAMEPAD_BUTTON_INVALID,
|
||||
|
||||
SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_A,
|
||||
SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_B,
|
||||
SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_X,
|
||||
SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_Y,
|
||||
SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_LEFTSTICK,
|
||||
SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_RIGHTSTICK,
|
||||
SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_LEFTSHOULDER,
|
||||
SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_RIGHTSHOULDER,
|
||||
SDL_GamepadButton.SDL_GAMEPAD_BUTTON_EAST,
|
||||
SDL_GamepadButton.SDL_GAMEPAD_BUTTON_SOUTH,
|
||||
SDL_GamepadButton.SDL_GAMEPAD_BUTTON_NORTH,
|
||||
SDL_GamepadButton.SDL_GAMEPAD_BUTTON_WEST,
|
||||
SDL_GamepadButton.SDL_GAMEPAD_BUTTON_LEFT_STICK,
|
||||
SDL_GamepadButton.SDL_GAMEPAD_BUTTON_RIGHT_STICK,
|
||||
SDL_GamepadButton.SDL_GAMEPAD_BUTTON_LEFT_SHOULDER,
|
||||
SDL_GamepadButton.SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER,
|
||||
|
||||
// NOTE: The left and right trigger are axis, we handle those differently
|
||||
SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_INVALID,
|
||||
SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_INVALID,
|
||||
SDL_GamepadButton.SDL_GAMEPAD_BUTTON_INVALID,
|
||||
SDL_GamepadButton.SDL_GAMEPAD_BUTTON_INVALID,
|
||||
|
||||
SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_DPAD_UP,
|
||||
SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_DPAD_DOWN,
|
||||
SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_DPAD_LEFT,
|
||||
SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_DPAD_RIGHT,
|
||||
SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_BACK,
|
||||
SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_START,
|
||||
SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_GUIDE,
|
||||
SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_MISC1,
|
||||
SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_PADDLE1,
|
||||
SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_PADDLE2,
|
||||
SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_PADDLE3,
|
||||
SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_PADDLE4,
|
||||
SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_TOUCHPAD,
|
||||
SDL_GamepadButton.SDL_GAMEPAD_BUTTON_DPAD_UP,
|
||||
SDL_GamepadButton.SDL_GAMEPAD_BUTTON_DPAD_DOWN,
|
||||
SDL_GamepadButton.SDL_GAMEPAD_BUTTON_DPAD_LEFT,
|
||||
SDL_GamepadButton.SDL_GAMEPAD_BUTTON_DPAD_RIGHT,
|
||||
SDL_GamepadButton.SDL_GAMEPAD_BUTTON_BACK,
|
||||
SDL_GamepadButton.SDL_GAMEPAD_BUTTON_START,
|
||||
SDL_GamepadButton.SDL_GAMEPAD_BUTTON_GUIDE,
|
||||
SDL_GamepadButton.SDL_GAMEPAD_BUTTON_MISC1,
|
||||
SDL_GamepadButton.SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1,
|
||||
SDL_GamepadButton.SDL_GAMEPAD_BUTTON_LEFT_PADDLE1,
|
||||
SDL_GamepadButton.SDL_GAMEPAD_BUTTON_RIGHT_PADDLE2,
|
||||
SDL_GamepadButton.SDL_GAMEPAD_BUTTON_LEFT_PADDLE2,
|
||||
SDL_GamepadButton.SDL_GAMEPAD_BUTTON_TOUCHPAD,
|
||||
|
||||
// Virtual buttons are invalid, ignored.
|
||||
SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_INVALID,
|
||||
SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_INVALID,
|
||||
SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_INVALID,
|
||||
SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_INVALID
|
||||
SDL_GamepadButton.SDL_GAMEPAD_BUTTON_INVALID,
|
||||
SDL_GamepadButton.SDL_GAMEPAD_BUTTON_INVALID,
|
||||
SDL_GamepadButton.SDL_GAMEPAD_BUTTON_INVALID,
|
||||
SDL_GamepadButton.SDL_GAMEPAD_BUTTON_INVALID,
|
||||
];
|
||||
|
||||
private readonly Lock _userMappingLock = new();
|
||||
@@ -73,29 +74,29 @@ namespace Ryujinx.Input.SDL2
|
||||
|
||||
public GamepadFeaturesFlag Features { get; }
|
||||
|
||||
private nint _gamepadHandle;
|
||||
private SDL_Gamepad* _gamepadHandle;
|
||||
|
||||
private float _triggerThreshold;
|
||||
|
||||
public SDL2Gamepad(nint gamepadHandle, string driverId)
|
||||
public SDL3Gamepad(SDL_Gamepad* gamepadHandle, string driverId)
|
||||
{
|
||||
_gamepadHandle = gamepadHandle;
|
||||
_buttonsUserMapping = new List<ButtonMappingEntry>(20);
|
||||
|
||||
Name = SDL_GameControllerName(_gamepadHandle);
|
||||
Name = SDL_GetGamepadName(_gamepadHandle);
|
||||
Id = driverId;
|
||||
Features = GetFeaturesFlag();
|
||||
_triggerThreshold = 0.0f;
|
||||
|
||||
// Enable motion tracking
|
||||
if (Features.HasFlag(GamepadFeaturesFlag.Motion))
|
||||
if ((Features & GamepadFeaturesFlag.Motion) != 0)
|
||||
{
|
||||
if (SDL_GameControllerSetSensorEnabled(_gamepadHandle, SDL_SensorType.SDL_SENSOR_ACCEL, SDL_bool.SDL_TRUE) != 0)
|
||||
if (!SDL_SetGamepadSensorEnabled(_gamepadHandle, SDL_SensorType.SDL_SENSOR_ACCEL, true))
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Hid, $"Could not enable data reporting for SensorType {SDL_SensorType.SDL_SENSOR_ACCEL}.");
|
||||
}
|
||||
|
||||
if (SDL_GameControllerSetSensorEnabled(_gamepadHandle, SDL_SensorType.SDL_SENSOR_GYRO, SDL_bool.SDL_TRUE) != 0)
|
||||
if (!SDL_SetGamepadSensorEnabled(_gamepadHandle, SDL_SensorType.SDL_SENSOR_GYRO, true))
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Hid, $"Could not enable data reporting for SensorType {SDL_SensorType.SDL_SENSOR_GYRO}.");
|
||||
}
|
||||
@@ -104,14 +105,14 @@ namespace Ryujinx.Input.SDL2
|
||||
|
||||
public void SetLed(uint packedRgb)
|
||||
{
|
||||
if (!Features.HasFlag(GamepadFeaturesFlag.Led))
|
||||
if ((Features & GamepadFeaturesFlag.Led) == 0)
|
||||
return;
|
||||
|
||||
byte red = packedRgb > 0 ? (byte)(packedRgb >> 16) : (byte)0;
|
||||
byte green = packedRgb > 0 ? (byte)(packedRgb >> 8) : (byte)0;
|
||||
byte blue = packedRgb > 0 ? (byte)(packedRgb % 256) : (byte)0;
|
||||
|
||||
if (SDL_GameControllerSetLED(_gamepadHandle, red, green, blue) != 0)
|
||||
if (!SDL_SetGamepadLED(_gamepadHandle, red, green, blue))
|
||||
Logger.Debug?.Print(LogClass.Hid, "LED setting failed; probably in the middle of disconnecting.");
|
||||
}
|
||||
|
||||
@@ -119,21 +120,24 @@ namespace Ryujinx.Input.SDL2
|
||||
{
|
||||
GamepadFeaturesFlag result = GamepadFeaturesFlag.None;
|
||||
|
||||
if (SDL_GameControllerHasSensor(_gamepadHandle, SDL_SensorType.SDL_SENSOR_ACCEL) == SDL_bool.SDL_TRUE &&
|
||||
SDL_GameControllerHasSensor(_gamepadHandle, SDL_SensorType.SDL_SENSOR_GYRO) == SDL_bool.SDL_TRUE)
|
||||
if (SDL_GamepadHasSensor(_gamepadHandle, SDL_SensorType.SDL_SENSOR_ACCEL) &&
|
||||
SDL_GamepadHasSensor(_gamepadHandle, SDL_SensorType.SDL_SENSOR_GYRO))
|
||||
{
|
||||
result |= GamepadFeaturesFlag.Motion;
|
||||
}
|
||||
|
||||
if (SDL_GameControllerHasRumble(_gamepadHandle) == SDL_bool.SDL_TRUE)
|
||||
SDL_PropertiesID propID = SDL_GetGamepadProperties(_gamepadHandle);
|
||||
SDL_LockProperties(propID);
|
||||
if (SDL_GetBooleanProperty(propID, SDL_PROP_GAMEPAD_CAP_RUMBLE_BOOLEAN, false))
|
||||
{
|
||||
result |= GamepadFeaturesFlag.Rumble;
|
||||
}
|
||||
|
||||
if (SDL_GameControllerHasLED(_gamepadHandle) == SDL_bool.SDL_TRUE)
|
||||
if (SDL_GetBooleanProperty(propID, SDL_PROP_GAMEPAD_CAP_MONO_LED_BOOLEAN, false))
|
||||
{
|
||||
result |= GamepadFeaturesFlag.Led;
|
||||
}
|
||||
SDL_UnlockProperties(propID);
|
||||
SDL_DestroyProperties(propID);
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -141,15 +145,15 @@ namespace Ryujinx.Input.SDL2
|
||||
public string Id { get; }
|
||||
public string Name { get; }
|
||||
|
||||
public bool IsConnected => SDL_GameControllerGetAttached(_gamepadHandle) == SDL_bool.SDL_TRUE;
|
||||
public bool IsConnected => SDL_GamepadConnected(_gamepadHandle);
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && _gamepadHandle != nint.Zero)
|
||||
if (disposing && _gamepadHandle != null)
|
||||
{
|
||||
SDL_GameControllerClose(_gamepadHandle);
|
||||
SDL_CloseGamepad(_gamepadHandle);
|
||||
|
||||
_gamepadHandle = nint.Zero;
|
||||
_gamepadHandle = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,7 +170,7 @@ namespace Ryujinx.Input.SDL2
|
||||
|
||||
public void Rumble(float lowFrequency, float highFrequency, uint durationMs)
|
||||
{
|
||||
if (!Features.HasFlag(GamepadFeaturesFlag.Rumble))
|
||||
if ((Features & GamepadFeaturesFlag.Rumble) == 0)
|
||||
return;
|
||||
|
||||
ushort lowFrequencyRaw = (ushort)(lowFrequency * ushort.MaxValue);
|
||||
@@ -174,7 +178,7 @@ namespace Ryujinx.Input.SDL2
|
||||
|
||||
if (durationMs == uint.MaxValue)
|
||||
{
|
||||
if (SDL_GameControllerRumble(_gamepadHandle, lowFrequencyRaw, highFrequencyRaw, SDL_HAPTIC_INFINITY) != 0)
|
||||
if (!SDL_RumbleGamepad(_gamepadHandle, lowFrequencyRaw, highFrequencyRaw, SDL_HAPTIC_INFINITY))
|
||||
Logger.Error?.Print(LogClass.Hid, "Rumble is not supported on this game controller.");
|
||||
}
|
||||
else if (durationMs > SDL_HAPTIC_INFINITY)
|
||||
@@ -183,7 +187,7 @@ namespace Ryujinx.Input.SDL2
|
||||
}
|
||||
else
|
||||
{
|
||||
if (SDL_GameControllerRumble(_gamepadHandle, lowFrequencyRaw, highFrequencyRaw, durationMs) != 0)
|
||||
if (!SDL_RumbleGamepad(_gamepadHandle, lowFrequencyRaw, highFrequencyRaw, durationMs))
|
||||
Logger.Error?.Print(LogClass.Hid, "Rumble is not supported on this game controller.");
|
||||
}
|
||||
}
|
||||
@@ -202,13 +206,11 @@ namespace Ryujinx.Input.SDL2
|
||||
|
||||
const int ElementCount = 3;
|
||||
|
||||
unsafe
|
||||
{
|
||||
float* values = stackalloc float[ElementCount];
|
||||
float[] values = new float[3];
|
||||
|
||||
int result = SDL_GameControllerGetSensorData(_gamepadHandle, sensorType, (nint)values, ElementCount);
|
||||
fixed (float* pValues = &values[0]) {
|
||||
|
||||
if (result != 0)
|
||||
if (!SDL_GetGamepadSensorData(_gamepadHandle, sensorType, pValues, ElementCount))
|
||||
return Vector3.Zero;
|
||||
|
||||
Vector3 value = new(values[0], values[1], values[2]);
|
||||
@@ -232,7 +234,7 @@ namespace Ryujinx.Input.SDL2
|
||||
{
|
||||
_configuration = (StandardControllerInputConfig)configuration;
|
||||
|
||||
if (Features.HasFlag(GamepadFeaturesFlag.Led) && _configuration.Led.EnableLed)
|
||||
if ((Features & GamepadFeaturesFlag.Led) != 0 && _configuration.Led.EnableLed)
|
||||
{
|
||||
if (_configuration.Led.TurnOffLed)
|
||||
(this as IGamepad).ClearLed();
|
||||
@@ -380,11 +382,11 @@ namespace Ryujinx.Input.SDL2
|
||||
inputId switch
|
||||
{
|
||||
StickInputId.Left => (
|
||||
SDL_GameControllerGetAxis(_gamepadHandle, SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_LEFTX),
|
||||
SDL_GameControllerGetAxis(_gamepadHandle, SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_LEFTY)),
|
||||
SDL_GetGamepadAxis(_gamepadHandle, SDL_GamepadAxis.SDL_GAMEPAD_AXIS_LEFTX),
|
||||
SDL_GetGamepadAxis(_gamepadHandle, SDL_GamepadAxis.SDL_GAMEPAD_AXIS_LEFTY)),
|
||||
StickInputId.Right => (
|
||||
SDL_GameControllerGetAxis(_gamepadHandle, SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_RIGHTX),
|
||||
SDL_GameControllerGetAxis(_gamepadHandle, SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_RIGHTY)),
|
||||
SDL_GetGamepadAxis(_gamepadHandle, SDL_GamepadAxis.SDL_GAMEPAD_AXIS_RIGHTX),
|
||||
SDL_GetGamepadAxis(_gamepadHandle, SDL_GamepadAxis.SDL_GAMEPAD_AXIS_RIGHTY)),
|
||||
_ => throw new NotSupportedException($"Unsupported stick {inputId}")
|
||||
};
|
||||
|
||||
@@ -393,17 +395,17 @@ namespace Ryujinx.Input.SDL2
|
||||
switch (inputId)
|
||||
{
|
||||
case GamepadButtonInputId.LeftTrigger:
|
||||
return ConvertRawStickValue(SDL_GameControllerGetAxis(_gamepadHandle, SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_TRIGGERLEFT)) > _triggerThreshold;
|
||||
return ConvertRawStickValue(SDL_GetGamepadAxis(_gamepadHandle, SDL_GamepadAxis.SDL_GAMEPAD_AXIS_LEFT_TRIGGER)) > _triggerThreshold;
|
||||
case GamepadButtonInputId.RightTrigger:
|
||||
return ConvertRawStickValue(SDL_GameControllerGetAxis(_gamepadHandle, SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_TRIGGERRIGHT)) > _triggerThreshold;
|
||||
return ConvertRawStickValue(SDL_GetGamepadAxis(_gamepadHandle, SDL_GamepadAxis.SDL_GAMEPAD_AXIS_RIGHT_TRIGGER)) > _triggerThreshold;
|
||||
}
|
||||
|
||||
if (_buttonsDriverMapping[(int)inputId] == SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_INVALID)
|
||||
if (_buttonsDriverMapping[(int)inputId] == SDL_GamepadButton.SDL_GAMEPAD_BUTTON_INVALID)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return SDL_GameControllerGetButton(_gamepadHandle, _buttonsDriverMapping[(int)inputId]) == 1;
|
||||
return SDL_GetGamepadButton(_gamepadHandle, _buttonsDriverMapping[(int)inputId]);
|
||||
}
|
||||
}
|
||||
}
|
||||
248
src/Ryujinx.Input.SDL3/SDL3GamepadDriver.cs
Normal file
248
src/Ryujinx.Input.SDL3/SDL3GamepadDriver.cs
Normal file
@@ -0,0 +1,248 @@
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.SDL3.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using SDL;
|
||||
using System.Linq;
|
||||
using static SDL.SDL3;
|
||||
|
||||
namespace Ryujinx.Input.SDL3
|
||||
{
|
||||
public unsafe class SDL3GamepadDriver : IGamepadDriver
|
||||
{
|
||||
private readonly Dictionary<SDL_JoystickID, string> _gamepadsInstanceIdsMapping;
|
||||
private readonly Dictionary<SDL_JoystickID, string> _gamepadsIds;
|
||||
private readonly Lock _lock = new();
|
||||
|
||||
public ReadOnlySpan<string> GamepadsIds
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return _gamepadsIds.Values.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string DriverName => "SDL3";
|
||||
|
||||
public event Action<string> OnGamepadConnected;
|
||||
public event Action<string> OnGamepadDisconnected;
|
||||
|
||||
public SDL3GamepadDriver()
|
||||
{
|
||||
_gamepadsInstanceIdsMapping = new Dictionary<SDL_JoystickID, string>();
|
||||
_gamepadsIds = [];
|
||||
|
||||
SDL3Driver.Instance.Initialize();
|
||||
SDL3Driver.Instance.OnJoyStickConnected += HandleJoyStickConnected;
|
||||
SDL3Driver.Instance.OnJoystickDisconnected += HandleJoyStickDisconnected;
|
||||
SDL3Driver.Instance.OnJoyBatteryUpdated += HandleJoyBatteryUpdated;
|
||||
|
||||
// Add already connected gamepads
|
||||
int joystickCount = 0;
|
||||
|
||||
SDL_JoystickID* pJoystickInstanceIds = SDL_GetJoysticks(&joystickCount);
|
||||
|
||||
for (int i = 0; i < joystickCount; i++)
|
||||
{
|
||||
HandleJoyStickConnected(pJoystickInstanceIds[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe static string SDLGuidToString(SDL_GUID guid)
|
||||
{
|
||||
string map = "0123456789abcdef";
|
||||
char[] guidBytes = new char[33];
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
byte c = guid.data[i];
|
||||
guidBytes[i * 2] = map[c >> 4];
|
||||
guidBytes[(i * 2) + 1] = map[c & 0x0f];
|
||||
}
|
||||
|
||||
string strGuid = new(guidBytes);
|
||||
|
||||
return $"{strGuid[0..8]}-{strGuid[8..12]}-{strGuid[12..16]}-{strGuid[16..20]}-{strGuid[20..32]}";
|
||||
|
||||
}
|
||||
|
||||
private unsafe string GenerateGamepadId(SDL_JoystickID joystickInstanceId)
|
||||
{
|
||||
SDL_GUID sdlGuid = SDL_GetJoystickGUIDForID(joystickInstanceId);
|
||||
string guidBytes = SDLGuidToString(sdlGuid);
|
||||
Guid guid = Guid.Parse(guidBytes);
|
||||
|
||||
// Add a unique identifier to the start of the GUID in case of duplicates.
|
||||
|
||||
if (guid == Guid.Empty)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Remove the first 4 char of the guid (CRC part) to make it stable
|
||||
string guidString = $"0000{guid.ToString()[4..]}";
|
||||
|
||||
string id;
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
int guidIndex = 0;
|
||||
id = guidIndex + "-" + guidString;
|
||||
|
||||
while (_gamepadsIds.ContainsValue(id))
|
||||
{
|
||||
id = (++guidIndex) + "-" + guidString;
|
||||
}
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
private void HandleJoyStickDisconnected(SDL_JoystickID joystickInstanceId)
|
||||
{
|
||||
bool joyConPairDisconnected = false;
|
||||
|
||||
if (!_gamepadsInstanceIdsMapping.Remove(joystickInstanceId, out string id))
|
||||
return;
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
_gamepadsIds.Remove(joystickInstanceId);
|
||||
if (!SDL3JoyConPair.IsCombinable(_gamepadsIds))
|
||||
{
|
||||
_gamepadsIds.Remove(GetInstanceIdFromId(SDL3JoyConPair.Id));
|
||||
joyConPairDisconnected = true;
|
||||
}
|
||||
}
|
||||
|
||||
OnGamepadDisconnected?.Invoke(id);
|
||||
if (joyConPairDisconnected)
|
||||
{
|
||||
OnGamepadDisconnected?.Invoke(SDL3JoyConPair.Id);
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleJoyStickConnected(SDL_JoystickID joystickInstanceId)
|
||||
{
|
||||
bool joyConPairConnected = false;
|
||||
|
||||
if (SDL_IsGamepad(joystickInstanceId))
|
||||
{
|
||||
if (_gamepadsInstanceIdsMapping.ContainsKey(joystickInstanceId))
|
||||
{
|
||||
// Sometimes a JoyStick connected event fires after the app starts even though it was connected before
|
||||
// so it is rejected to avoid doubling the entries.
|
||||
return;
|
||||
}
|
||||
|
||||
string id = GenerateGamepadId(joystickInstanceId);
|
||||
|
||||
if (id == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_gamepadsInstanceIdsMapping.TryAdd(joystickInstanceId, id))
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
|
||||
_gamepadsIds.Add(joystickInstanceId, id);
|
||||
|
||||
if (SDL3JoyConPair.IsCombinable(_gamepadsIds))
|
||||
{
|
||||
_gamepadsIds.Remove(GetInstanceIdFromId(SDL3JoyConPair.Id));
|
||||
_gamepadsIds.Add(joystickInstanceId, SDL3JoyConPair.Id);
|
||||
joyConPairConnected = true;
|
||||
}
|
||||
}
|
||||
|
||||
OnGamepadConnected?.Invoke(id);
|
||||
if (joyConPairConnected)
|
||||
{
|
||||
OnGamepadConnected?.Invoke(SDL3JoyConPair.Id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleJoyBatteryUpdated(SDL_JoystickID joystickInstanceId, SDL_PowerState powerLevel)
|
||||
{
|
||||
Logger.Info?.Print(LogClass.Hid,
|
||||
$"{SDL_GetGamepadNameForID(joystickInstanceId)} power level: {powerLevel}");
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
SDL3Driver.Instance.OnJoyStickConnected -= HandleJoyStickConnected;
|
||||
SDL3Driver.Instance.OnJoystickDisconnected -= HandleJoyStickDisconnected;
|
||||
|
||||
// Simulate a full disconnect when disposing
|
||||
foreach (var gamepad in _gamepadsIds)
|
||||
{
|
||||
OnGamepadDisconnected?.Invoke(gamepad.Value);
|
||||
}
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
_gamepadsIds.Clear();
|
||||
}
|
||||
|
||||
SDL3Driver.Instance.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
GC.SuppressFinalize(this);
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
public SDL_JoystickID GetInstanceIdFromId(string id) {
|
||||
return _gamepadsInstanceIdsMapping.Where(e => e.Value == id).FirstOrDefault().Key;
|
||||
}
|
||||
|
||||
public IGamepad GetGamepad(string id)
|
||||
{
|
||||
if (id == SDL3JoyConPair.Id)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return SDL3JoyConPair.GetGamepad(_gamepadsIds);
|
||||
}
|
||||
}
|
||||
|
||||
SDL_JoystickID instanceId = GetInstanceIdFromId(id);
|
||||
|
||||
SDL_Gamepad* gamepadHandle = SDL_OpenGamepad(instanceId);
|
||||
|
||||
if (gamepadHandle == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (SDL_GetGamepadName(gamepadHandle).StartsWith(SDL3JoyCon.Prefix))
|
||||
{
|
||||
return new SDL3JoyCon(gamepadHandle, id);
|
||||
}
|
||||
|
||||
return new SDL3Gamepad(gamepadHandle, id);
|
||||
}
|
||||
|
||||
public IEnumerable<IGamepad> GetGamepads()
|
||||
{
|
||||
lock (_gamepadsIds)
|
||||
{
|
||||
foreach (var gamepad in _gamepadsIds)
|
||||
{
|
||||
yield return GetGamepad(gamepad.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,11 +5,12 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using System.Threading;
|
||||
using static SDL2.SDL;
|
||||
using SDL;
|
||||
using static SDL.SDL3;
|
||||
|
||||
namespace Ryujinx.Input.SDL2
|
||||
namespace Ryujinx.Input.SDL3
|
||||
{
|
||||
internal class SDL2JoyCon : IGamepad
|
||||
internal unsafe class SDL3JoyCon : IGamepad
|
||||
{
|
||||
private bool HasConfiguration => _configuration != null;
|
||||
|
||||
@@ -20,34 +21,34 @@ namespace Ryujinx.Input.SDL2
|
||||
|
||||
private StandardControllerInputConfig _configuration;
|
||||
|
||||
private readonly Dictionary<GamepadButtonInputId, SDL_GameControllerButton> _leftButtonsDriverMapping = new()
|
||||
private readonly Dictionary<GamepadButtonInputId, SDL_GamepadButton> _leftButtonsDriverMapping = new()
|
||||
{
|
||||
{ GamepadButtonInputId.LeftStick , SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_LEFTSTICK },
|
||||
{GamepadButtonInputId.DpadUp ,SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_Y},
|
||||
{GamepadButtonInputId.DpadDown ,SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_A},
|
||||
{GamepadButtonInputId.DpadLeft ,SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_B},
|
||||
{GamepadButtonInputId.DpadRight ,SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_X},
|
||||
{GamepadButtonInputId.Minus ,SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_START},
|
||||
{GamepadButtonInputId.LeftShoulder,SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_PADDLE2},
|
||||
{GamepadButtonInputId.LeftTrigger,SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_PADDLE4},
|
||||
{GamepadButtonInputId.SingleRightTrigger0,SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
|
||||
{GamepadButtonInputId.SingleLeftTrigger0,SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
|
||||
{GamepadButtonInputId.LeftStick, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_LEFT_STICK},
|
||||
{GamepadButtonInputId.DpadUp, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_NORTH},
|
||||
{GamepadButtonInputId.DpadDown, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_SOUTH},
|
||||
{GamepadButtonInputId.DpadLeft, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_EAST},
|
||||
{GamepadButtonInputId.DpadRight, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_WEST},
|
||||
{GamepadButtonInputId.Minus, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_START},
|
||||
{GamepadButtonInputId.LeftShoulder, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_LEFT_PADDLE1},
|
||||
{GamepadButtonInputId.LeftTrigger, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_LEFT_PADDLE2},
|
||||
{GamepadButtonInputId.SingleRightTrigger0, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER},
|
||||
{GamepadButtonInputId.SingleLeftTrigger0, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_LEFT_SHOULDER},
|
||||
};
|
||||
private readonly Dictionary<GamepadButtonInputId, SDL_GameControllerButton> _rightButtonsDriverMapping = new()
|
||||
private readonly Dictionary<GamepadButtonInputId, SDL_GamepadButton> _rightButtonsDriverMapping = new()
|
||||
{
|
||||
{GamepadButtonInputId.RightStick,SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_LEFTSTICK},
|
||||
{GamepadButtonInputId.A,SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_B},
|
||||
{GamepadButtonInputId.B,SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_Y},
|
||||
{GamepadButtonInputId.X,SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_A},
|
||||
{GamepadButtonInputId.Y,SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_X},
|
||||
{GamepadButtonInputId.Plus,SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_START},
|
||||
{GamepadButtonInputId.RightShoulder,SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_PADDLE1},
|
||||
{GamepadButtonInputId.RightTrigger,SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_PADDLE3},
|
||||
{GamepadButtonInputId.SingleRightTrigger1,SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
|
||||
{GamepadButtonInputId.SingleLeftTrigger1,SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_LEFTSHOULDER}
|
||||
{GamepadButtonInputId.RightStick, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_LEFT_STICK},
|
||||
{GamepadButtonInputId.A, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_EAST},
|
||||
{GamepadButtonInputId.B, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_NORTH},
|
||||
{GamepadButtonInputId.X, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_SOUTH},
|
||||
{GamepadButtonInputId.Y, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_WEST},
|
||||
{GamepadButtonInputId.Plus, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_START},
|
||||
{GamepadButtonInputId.RightShoulder, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1},
|
||||
{GamepadButtonInputId.RightTrigger, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_RIGHT_PADDLE2},
|
||||
{GamepadButtonInputId.SingleRightTrigger1, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER},
|
||||
{GamepadButtonInputId.SingleLeftTrigger1, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_LEFT_SHOULDER}
|
||||
};
|
||||
|
||||
private readonly Dictionary<GamepadButtonInputId, SDL_GameControllerButton> _buttonsDriverMapping;
|
||||
private readonly Dictionary<GamepadButtonInputId, SDL_GamepadButton> _buttonsDriverMapping;
|
||||
private readonly Lock _userMappingLock = new();
|
||||
|
||||
private readonly List<ButtonMappingEntry> _buttonsUserMapping;
|
||||
@@ -59,7 +60,7 @@ namespace Ryujinx.Input.SDL2
|
||||
|
||||
public GamepadFeaturesFlag Features { get; }
|
||||
|
||||
private nint _gamepadHandle;
|
||||
private SDL_Gamepad* _gamepadHandle;
|
||||
|
||||
private enum JoyConType
|
||||
{
|
||||
@@ -72,27 +73,25 @@ namespace Ryujinx.Input.SDL2
|
||||
|
||||
private readonly JoyConType _joyConType;
|
||||
|
||||
public SDL2JoyCon(nint gamepadHandle, string driverId)
|
||||
public SDL3JoyCon(SDL_Gamepad* gamepadHandle, string driverId)
|
||||
{
|
||||
_gamepadHandle = gamepadHandle;
|
||||
_buttonsUserMapping = new List<ButtonMappingEntry>(10);
|
||||
|
||||
Name = SDL_GameControllerName(_gamepadHandle);
|
||||
Name = SDL_GetGamepadName(_gamepadHandle);
|
||||
Id = driverId;
|
||||
Features = GetFeaturesFlag();
|
||||
|
||||
// Enable motion tracking
|
||||
if (Features.HasFlag(GamepadFeaturesFlag.Motion))
|
||||
if ((Features & GamepadFeaturesFlag.Motion) != 0)
|
||||
{
|
||||
if (SDL_GameControllerSetSensorEnabled(_gamepadHandle, SDL_SensorType.SDL_SENSOR_ACCEL,
|
||||
SDL_bool.SDL_TRUE) != 0)
|
||||
if (!SDL_SetGamepadSensorEnabled(_gamepadHandle, SDL_SensorType.SDL_SENSOR_ACCEL, true))
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Hid,
|
||||
$"Could not enable data reporting for SensorType {SDL_SensorType.SDL_SENSOR_ACCEL}.");
|
||||
}
|
||||
|
||||
if (SDL_GameControllerSetSensorEnabled(_gamepadHandle, SDL_SensorType.SDL_SENSOR_GYRO,
|
||||
SDL_bool.SDL_TRUE) != 0)
|
||||
if (!SDL_SetGamepadSensorEnabled(_gamepadHandle, SDL_SensorType.SDL_SENSOR_GYRO, true))
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Hid,
|
||||
$"Could not enable data reporting for SensorType {SDL_SensorType.SDL_SENSOR_GYRO}.");
|
||||
@@ -120,15 +119,13 @@ namespace Ryujinx.Input.SDL2
|
||||
{
|
||||
GamepadFeaturesFlag result = GamepadFeaturesFlag.None;
|
||||
|
||||
if (SDL_GameControllerHasSensor(_gamepadHandle, SDL_SensorType.SDL_SENSOR_ACCEL) == SDL_bool.SDL_TRUE &&
|
||||
SDL_GameControllerHasSensor(_gamepadHandle, SDL_SensorType.SDL_SENSOR_GYRO) == SDL_bool.SDL_TRUE)
|
||||
if (SDL_GamepadHasSensor(_gamepadHandle, SDL_SensorType.SDL_SENSOR_ACCEL) &&
|
||||
SDL_GamepadHasSensor(_gamepadHandle, SDL_SensorType.SDL_SENSOR_GYRO))
|
||||
{
|
||||
result |= GamepadFeaturesFlag.Motion;
|
||||
}
|
||||
|
||||
int error = SDL_GameControllerRumble(_gamepadHandle, 0, 0, 100);
|
||||
|
||||
if (error == 0)
|
||||
if (SDL_RumbleGamepad(_gamepadHandle, 0, 0, 100))
|
||||
{
|
||||
result |= GamepadFeaturesFlag.Rumble;
|
||||
}
|
||||
@@ -138,15 +135,15 @@ namespace Ryujinx.Input.SDL2
|
||||
|
||||
public string Id { get; }
|
||||
public string Name { get; }
|
||||
public bool IsConnected => SDL_GameControllerGetAttached(_gamepadHandle) == SDL_bool.SDL_TRUE;
|
||||
public bool IsConnected => SDL_GamepadConnected(_gamepadHandle);
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && _gamepadHandle != nint.Zero)
|
||||
if (disposing && _gamepadHandle != null)
|
||||
{
|
||||
SDL_GameControllerClose(_gamepadHandle);
|
||||
SDL_CloseGamepad(_gamepadHandle);
|
||||
|
||||
_gamepadHandle = nint.Zero;
|
||||
_gamepadHandle = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,7 +159,7 @@ namespace Ryujinx.Input.SDL2
|
||||
|
||||
public void Rumble(float lowFrequency, float highFrequency, uint durationMs)
|
||||
{
|
||||
if (!Features.HasFlag(GamepadFeaturesFlag.Rumble))
|
||||
if ((Features & GamepadFeaturesFlag.Rumble) == 0)
|
||||
return;
|
||||
|
||||
ushort lowFrequencyRaw = (ushort)(lowFrequency * ushort.MaxValue);
|
||||
@@ -170,8 +167,7 @@ namespace Ryujinx.Input.SDL2
|
||||
|
||||
if (durationMs == uint.MaxValue)
|
||||
{
|
||||
if (SDL_GameControllerRumble(_gamepadHandle, lowFrequencyRaw, highFrequencyRaw, SDL_HAPTIC_INFINITY) !=
|
||||
0)
|
||||
if (!SDL_RumbleGamepad(_gamepadHandle, lowFrequencyRaw, highFrequencyRaw, SDL_HAPTIC_INFINITY))
|
||||
Logger.Error?.Print(LogClass.Hid, "Rumble is not supported on this game controller.");
|
||||
}
|
||||
else if (durationMs > SDL_HAPTIC_INFINITY)
|
||||
@@ -180,7 +176,7 @@ namespace Ryujinx.Input.SDL2
|
||||
}
|
||||
else
|
||||
{
|
||||
if (SDL_GameControllerRumble(_gamepadHandle, lowFrequencyRaw, highFrequencyRaw, durationMs) != 0)
|
||||
if (!SDL_RumbleGamepad(_gamepadHandle, lowFrequencyRaw, highFrequencyRaw, durationMs))
|
||||
Logger.Error?.Print(LogClass.Hid, "Rumble is not supported on this game controller.");
|
||||
}
|
||||
}
|
||||
@@ -194,18 +190,15 @@ namespace Ryujinx.Input.SDL2
|
||||
_ => SDL_SensorType.SDL_SENSOR_INVALID
|
||||
};
|
||||
|
||||
if (!Features.HasFlag(GamepadFeaturesFlag.Motion) || sensorType is SDL_SensorType.SDL_SENSOR_INVALID)
|
||||
if ((Features & GamepadFeaturesFlag.Motion) == 0 || sensorType is SDL_SensorType.SDL_SENSOR_INVALID)
|
||||
return Vector3.Zero;
|
||||
|
||||
const int ElementCount = 3;
|
||||
|
||||
unsafe
|
||||
{
|
||||
float* values = stackalloc float[ElementCount];
|
||||
float[] values = new float[3];
|
||||
|
||||
int result = SDL_GameControllerGetSensorData(_gamepadHandle, sensorType, (nint)values, ElementCount);
|
||||
|
||||
if (result != 0)
|
||||
fixed (float* pValues = &values[0]) {
|
||||
if (!SDL_GetGamepadSensorData(_gamepadHandle, sensorType, pValues, ElementCount))
|
||||
return Vector3.Zero;
|
||||
|
||||
Vector3 value = _joyConType switch
|
||||
@@ -392,18 +385,18 @@ namespace Ryujinx.Input.SDL2
|
||||
private (short, short) GetStickXY()
|
||||
{
|
||||
return (
|
||||
SDL_GameControllerGetAxis(_gamepadHandle, SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_LEFTX),
|
||||
SDL_GameControllerGetAxis(_gamepadHandle, SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_LEFTY));
|
||||
SDL_GetGamepadAxis(_gamepadHandle, SDL_GamepadAxis.SDL_GAMEPAD_AXIS_LEFTX),
|
||||
SDL_GetGamepadAxis(_gamepadHandle, SDL_GamepadAxis.SDL_GAMEPAD_AXIS_LEFTY));
|
||||
}
|
||||
|
||||
public bool IsPressed(GamepadButtonInputId inputId)
|
||||
{
|
||||
if (!_buttonsDriverMapping.TryGetValue(inputId, out SDL_GameControllerButton button))
|
||||
if (!_buttonsDriverMapping.TryGetValue(inputId, out SDL_GamepadButton button))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return SDL_GameControllerGetButton(_gamepadHandle, button) == 1;
|
||||
return SDL_GetGamepadButton(_gamepadHandle, button);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,11 +2,12 @@ using Ryujinx.Common.Configuration.Hid;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using static SDL2.SDL;
|
||||
using SDL;
|
||||
using static SDL.SDL3;
|
||||
|
||||
namespace Ryujinx.Input.SDL2
|
||||
namespace Ryujinx.Input.SDL3
|
||||
{
|
||||
internal class SDL2JoyConPair(IGamepad left, IGamepad right) : IGamepad
|
||||
internal class SDL3JoyConPair(IGamepad left, IGamepad right) : IGamepad
|
||||
{
|
||||
public GamepadFeaturesFlag Features => (left?.Features ?? GamepadFeaturesFlag.None) |
|
||||
(right?.Features ?? GamepadFeaturesFlag.None);
|
||||
@@ -95,40 +96,44 @@ namespace Ryujinx.Input.SDL2
|
||||
right.SetTriggerThreshold(triggerThreshold);
|
||||
}
|
||||
|
||||
public static bool IsCombinable(List<string> gamepadsIds)
|
||||
public static bool IsCombinable(Dictionary<SDL_JoystickID, string> gamepadsIds)
|
||||
{
|
||||
(int leftIndex, int rightIndex) = DetectJoyConPair(gamepadsIds);
|
||||
return leftIndex >= 0 && rightIndex >= 0;
|
||||
}
|
||||
|
||||
private static (int leftIndex, int rightIndex) DetectJoyConPair(List<string> gamepadsIds)
|
||||
private static (int leftIndex, int rightIndex) DetectJoyConPair(Dictionary<SDL_JoystickID, string> gamepadsIds)
|
||||
{
|
||||
List<string> gamepadNames = gamepadsIds.Where(gamepadId => gamepadId != Id)
|
||||
.Select((_, index) => SDL_GameControllerNameForIndex(index)).ToList();
|
||||
int leftIndex = gamepadNames.IndexOf(SDL2JoyCon.LeftName);
|
||||
int rightIndex = gamepadNames.IndexOf(SDL2JoyCon.RightName);
|
||||
Dictionary<string, SDL_JoystickID> gamepadNames = gamepadsIds
|
||||
.Where(gamepadId => gamepadId.Value != Id && SDL_GetGamepadNameForID(gamepadId.Key) is SDL3JoyCon.LeftName or SDL3JoyCon.RightName)
|
||||
.Select(gamepad => (SDL_GetGamepadNameForID(gamepad.Key), gamepad.Key))
|
||||
.ToDictionary();
|
||||
SDL_JoystickID idx;
|
||||
int leftIndex = gamepadNames.TryGetValue(SDL3JoyCon.LeftName, out idx) ? (int)idx : -1;
|
||||
int rightIndex = gamepadNames.TryGetValue(SDL3JoyCon.LeftName, out idx) ? (int)idx : -1;
|
||||
|
||||
return (leftIndex, rightIndex);
|
||||
}
|
||||
|
||||
public static IGamepad GetGamepad(List<string> gamepadsIds)
|
||||
public unsafe static IGamepad GetGamepad(Dictionary<SDL_JoystickID, string> gamepadsIds)
|
||||
{
|
||||
(int leftIndex, int rightIndex) = DetectJoyConPair(gamepadsIds);
|
||||
if (leftIndex == -1 || rightIndex == -1)
|
||||
|
||||
if (leftIndex <= 0 || rightIndex <= 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
nint leftGamepadHandle = SDL_GameControllerOpen(leftIndex);
|
||||
nint rightGamepadHandle = SDL_GameControllerOpen(rightIndex);
|
||||
SDL_Gamepad* leftGamepadHandle = SDL_OpenGamepad((SDL_JoystickID)leftIndex);
|
||||
SDL_Gamepad* rightGamepadHandle = SDL_OpenGamepad((SDL_JoystickID)rightIndex);
|
||||
|
||||
if (leftGamepadHandle == nint.Zero || rightGamepadHandle == nint.Zero)
|
||||
if (leftGamepadHandle == null || rightGamepadHandle == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new SDL2JoyConPair(new SDL2JoyCon(leftGamepadHandle, gamepadsIds[leftIndex]),
|
||||
new SDL2JoyCon(rightGamepadHandle, gamepadsIds[rightIndex]));
|
||||
return new SDL3JoyConPair(new SDL3JoyCon(leftGamepadHandle, gamepadsIds[(SDL_JoystickID)leftIndex]),
|
||||
new SDL3JoyCon(rightGamepadHandle, gamepadsIds[(SDL_JoystickID)rightIndex]));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,13 +6,14 @@ using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using static SDL2.SDL;
|
||||
using SDL;
|
||||
using static SDL.SDL3;
|
||||
|
||||
using ConfigKey = Ryujinx.Common.Configuration.Hid.Key;
|
||||
|
||||
namespace Ryujinx.Input.SDL2
|
||||
namespace Ryujinx.Input.SDL3
|
||||
{
|
||||
class SDL2Keyboard : IKeyboard
|
||||
class SDL3Keyboard : IKeyboard
|
||||
{
|
||||
private readonly record struct ButtonMappingEntry(GamepadButtonInputId To, Key From)
|
||||
{
|
||||
@@ -22,11 +23,12 @@ namespace Ryujinx.Input.SDL2
|
||||
private readonly Lock _userMappingLock = new();
|
||||
|
||||
#pragma warning disable IDE0052 // Remove unread private member
|
||||
private readonly SDL2KeyboardDriver _driver;
|
||||
private readonly SDL3KeyboardDriver _driver;
|
||||
#pragma warning restore IDE0052
|
||||
private StandardKeyboardInputConfig _configuration;
|
||||
private readonly List<ButtonMappingEntry> _buttonsUserMapping;
|
||||
|
||||
|
||||
private static readonly SDL_Keycode[] _keysDriverMapping =
|
||||
[
|
||||
// INVALID
|
||||
@@ -116,32 +118,32 @@ namespace Ryujinx.Input.SDL2
|
||||
SDL_Keycode.SDLK_KP_PLUS,
|
||||
SDL_Keycode.SDLK_KP_DECIMAL,
|
||||
SDL_Keycode.SDLK_KP_ENTER,
|
||||
SDL_Keycode.SDLK_a,
|
||||
SDL_Keycode.SDLK_b,
|
||||
SDL_Keycode.SDLK_c,
|
||||
SDL_Keycode.SDLK_d,
|
||||
SDL_Keycode.SDLK_e,
|
||||
SDL_Keycode.SDLK_f,
|
||||
SDL_Keycode.SDLK_g,
|
||||
SDL_Keycode.SDLK_h,
|
||||
SDL_Keycode.SDLK_i,
|
||||
SDL_Keycode.SDLK_j,
|
||||
SDL_Keycode.SDLK_k,
|
||||
SDL_Keycode.SDLK_l,
|
||||
SDL_Keycode.SDLK_m,
|
||||
SDL_Keycode.SDLK_n,
|
||||
SDL_Keycode.SDLK_o,
|
||||
SDL_Keycode.SDLK_p,
|
||||
SDL_Keycode.SDLK_q,
|
||||
SDL_Keycode.SDLK_r,
|
||||
SDL_Keycode.SDLK_s,
|
||||
SDL_Keycode.SDLK_t,
|
||||
SDL_Keycode.SDLK_u,
|
||||
SDL_Keycode.SDLK_v,
|
||||
SDL_Keycode.SDLK_w,
|
||||
SDL_Keycode.SDLK_x,
|
||||
SDL_Keycode.SDLK_y,
|
||||
SDL_Keycode.SDLK_z,
|
||||
SDL_Keycode.SDLK_A,
|
||||
SDL_Keycode.SDLK_B,
|
||||
SDL_Keycode.SDLK_C,
|
||||
SDL_Keycode.SDLK_D,
|
||||
SDL_Keycode.SDLK_E,
|
||||
SDL_Keycode.SDLK_F,
|
||||
SDL_Keycode.SDLK_G,
|
||||
SDL_Keycode.SDLK_H,
|
||||
SDL_Keycode.SDLK_I,
|
||||
SDL_Keycode.SDLK_J,
|
||||
SDL_Keycode.SDLK_K,
|
||||
SDL_Keycode.SDLK_L,
|
||||
SDL_Keycode.SDLK_M,
|
||||
SDL_Keycode.SDLK_N,
|
||||
SDL_Keycode.SDLK_O,
|
||||
SDL_Keycode.SDLK_P,
|
||||
SDL_Keycode.SDLK_Q,
|
||||
SDL_Keycode.SDLK_R,
|
||||
SDL_Keycode.SDLK_S,
|
||||
SDL_Keycode.SDLK_T,
|
||||
SDL_Keycode.SDLK_U,
|
||||
SDL_Keycode.SDLK_V,
|
||||
SDL_Keycode.SDLK_W,
|
||||
SDL_Keycode.SDLK_X,
|
||||
SDL_Keycode.SDLK_Y,
|
||||
SDL_Keycode.SDLK_Z,
|
||||
SDL_Keycode.SDLK_0,
|
||||
SDL_Keycode.SDLK_1,
|
||||
SDL_Keycode.SDLK_2,
|
||||
@@ -152,14 +154,14 @@ namespace Ryujinx.Input.SDL2
|
||||
SDL_Keycode.SDLK_7,
|
||||
SDL_Keycode.SDLK_8,
|
||||
SDL_Keycode.SDLK_9,
|
||||
SDL_Keycode.SDLK_BACKQUOTE,
|
||||
SDL_Keycode.SDLK_BACKQUOTE,
|
||||
SDL_Keycode.SDLK_GRAVE,
|
||||
SDL_Keycode.SDLK_GRAVE,
|
||||
SDL_Keycode.SDLK_MINUS,
|
||||
SDL_Keycode.SDLK_PLUS,
|
||||
SDL_Keycode.SDLK_LEFTBRACKET,
|
||||
SDL_Keycode.SDLK_RIGHTBRACKET,
|
||||
SDL_Keycode.SDLK_SEMICOLON,
|
||||
SDL_Keycode.SDLK_QUOTE,
|
||||
SDL_Keycode.SDLK_APOSTROPHE,
|
||||
SDL_Keycode.SDLK_COMMA,
|
||||
SDL_Keycode.SDLK_PERIOD,
|
||||
SDL_Keycode.SDLK_SLASH,
|
||||
@@ -169,7 +171,7 @@ namespace Ryujinx.Input.SDL2
|
||||
SDL_Keycode.SDLK_0
|
||||
];
|
||||
|
||||
public SDL2Keyboard(SDL2KeyboardDriver driver, string id, string name)
|
||||
public SDL3Keyboard(SDL3KeyboardDriver driver, string id, string name)
|
||||
{
|
||||
_driver = driver;
|
||||
Id = id;
|
||||
@@ -193,55 +195,53 @@ namespace Ryujinx.Input.SDL2
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static int ToSDL2Scancode(Key key)
|
||||
private unsafe static int ToSDL3Scancode(Key key)
|
||||
{
|
||||
if (key is >= Key.Unknown and <= Key.Menu)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return (int)SDL_GetScancodeFromKey(_keysDriverMapping[(int)key]);
|
||||
return (int)SDL_GetScancodeFromKey(_keysDriverMapping[(int)key], null);
|
||||
}
|
||||
|
||||
private static SDL_Keymod GetKeyboardModifierMask(Key key)
|
||||
{
|
||||
return key switch
|
||||
{
|
||||
Key.ShiftLeft => SDL_Keymod.KMOD_LSHIFT,
|
||||
Key.ShiftRight => SDL_Keymod.KMOD_RSHIFT,
|
||||
Key.ControlLeft => SDL_Keymod.KMOD_LCTRL,
|
||||
Key.ControlRight => SDL_Keymod.KMOD_RCTRL,
|
||||
Key.AltLeft => SDL_Keymod.KMOD_LALT,
|
||||
Key.AltRight => SDL_Keymod.KMOD_RALT,
|
||||
Key.WinLeft => SDL_Keymod.KMOD_LGUI,
|
||||
Key.WinRight => SDL_Keymod.KMOD_RGUI,
|
||||
// NOTE: Menu key isn't supported by SDL2.
|
||||
_ => SDL_Keymod.KMOD_NONE,
|
||||
Key.ShiftLeft => SDL_Keymod.SDL_KMOD_LSHIFT,
|
||||
Key.ShiftRight => SDL_Keymod.SDL_KMOD_RSHIFT,
|
||||
Key.ControlLeft => SDL_Keymod.SDL_KMOD_LCTRL,
|
||||
Key.ControlRight => SDL_Keymod.SDL_KMOD_RCTRL,
|
||||
Key.AltLeft => SDL_Keymod.SDL_KMOD_LALT,
|
||||
Key.AltRight => SDL_Keymod.SDL_KMOD_RALT,
|
||||
Key.WinLeft => SDL_Keymod.SDL_KMOD_LGUI,
|
||||
Key.WinRight => SDL_Keymod.SDL_KMOD_RGUI,
|
||||
// NOTE: Menu key isn't supported by SDL3.
|
||||
_ => SDL_Keymod.SDL_KMOD_NONE
|
||||
};
|
||||
}
|
||||
|
||||
public KeyboardStateSnapshot GetKeyboardStateSnapshot()
|
||||
public unsafe KeyboardStateSnapshot GetKeyboardStateSnapshot()
|
||||
{
|
||||
ReadOnlySpan<byte> rawKeyboardState;
|
||||
SDLBool* rawKeyboardState;
|
||||
SDL_Keymod rawKeyboardModifierState = SDL_GetModState();
|
||||
|
||||
unsafe
|
||||
{
|
||||
nint statePtr = SDL_GetKeyboardState(out int numKeys);
|
||||
|
||||
rawKeyboardState = new ReadOnlySpan<byte>((byte*)statePtr, numKeys);
|
||||
rawKeyboardState = SDL_GetKeyboardState(null);
|
||||
}
|
||||
|
||||
bool[] keysState = new bool[(int)Key.Count];
|
||||
|
||||
for (Key key = 0; key < Key.Count; key++)
|
||||
{
|
||||
int index = ToSDL2Scancode(key);
|
||||
int index = ToSDL3Scancode(key);
|
||||
if (index == -1)
|
||||
{
|
||||
SDL_Keymod modifierMask = GetKeyboardModifierMask(key);
|
||||
|
||||
if (modifierMask == SDL_Keymod.KMOD_NONE)
|
||||
if (modifierMask == SDL_Keymod.SDL_KMOD_NONE)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -250,7 +250,7 @@ namespace Ryujinx.Input.SDL2
|
||||
}
|
||||
else
|
||||
{
|
||||
keysState[(int)key] = rawKeyboardState[index] == 1;
|
||||
keysState[(int)key] = rawKeyboardState[index];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -388,7 +388,7 @@ namespace Ryujinx.Input.SDL2
|
||||
|
||||
public void SetLed(uint packedRgb)
|
||||
{
|
||||
Logger.Info?.Print(LogClass.UI, "SetLed called on an SDL2Keyboard");
|
||||
Logger.Info?.Print(LogClass.UI, "SetLed called on an SDL3Keyboard");
|
||||
}
|
||||
|
||||
public void SetTriggerThreshold(float triggerThreshold)
|
||||
@@ -4,17 +4,17 @@ using System;
|
||||
using System.Drawing;
|
||||
using System.Numerics;
|
||||
|
||||
namespace Ryujinx.Input.SDL2
|
||||
namespace Ryujinx.Input.SDL3
|
||||
{
|
||||
public class SDL2Mouse : IMouse
|
||||
public class SDL3Mouse : IMouse
|
||||
{
|
||||
private SDL2MouseDriver _driver;
|
||||
private SDL3MouseDriver _driver;
|
||||
|
||||
public GamepadFeaturesFlag Features => throw new NotImplementedException();
|
||||
|
||||
public string Id => "0";
|
||||
|
||||
public string Name => "SDL2Mouse";
|
||||
public string Name => "SDL3Mouse";
|
||||
|
||||
public bool IsConnected => true;
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace Ryujinx.Input.SDL2
|
||||
|
||||
Size IMouse.ClientSize => _driver.GetClientSize();
|
||||
|
||||
public SDL2Mouse(SDL2MouseDriver driver)
|
||||
public SDL3Mouse(SDL3MouseDriver driver)
|
||||
{
|
||||
_driver = driver;
|
||||
}
|
||||
@@ -79,7 +79,7 @@ namespace Ryujinx.Input.SDL2
|
||||
|
||||
public void SetLed(uint packedRgb)
|
||||
{
|
||||
Logger.Info?.Print(LogClass.UI, "SetLed called on an SDL2Mouse");
|
||||
Logger.Info?.Print(LogClass.UI, "SetLed called on an SDL3Mouse");
|
||||
}
|
||||
|
||||
public void SetTriggerThreshold(float triggerThreshold)
|
||||
@@ -6,11 +6,12 @@ using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using static SDL2.SDL;
|
||||
using SDL;
|
||||
using static SDL.SDL3;
|
||||
|
||||
namespace Ryujinx.Input.SDL2
|
||||
namespace Ryujinx.Input.SDL3
|
||||
{
|
||||
public class SDL2MouseDriver : IGamepadDriver
|
||||
public class SDL3MouseDriver : IGamepadDriver
|
||||
{
|
||||
private const int CursorHideIdleTime = 5; // seconds
|
||||
|
||||
@@ -25,14 +26,14 @@ namespace Ryujinx.Input.SDL2
|
||||
public Vector2 Scroll { get; private set; }
|
||||
public Size ClientSize;
|
||||
|
||||
public SDL2MouseDriver(HideCursorMode hideCursorMode)
|
||||
public SDL3MouseDriver(HideCursorMode hideCursorMode)
|
||||
{
|
||||
PressedButtons = new bool[(int)MouseButton.Count];
|
||||
_hideCursorMode = hideCursorMode;
|
||||
|
||||
if (_hideCursorMode == HideCursorMode.Always)
|
||||
{
|
||||
if (SDL_ShowCursor(SDL_DISABLE) != SDL_DISABLE)
|
||||
if (!SDL_HideCursor())
|
||||
{
|
||||
Logger.Error?.PrintMsg(LogClass.Application, "Failed to disable the cursor.");
|
||||
}
|
||||
@@ -49,9 +50,11 @@ namespace Ryujinx.Input.SDL2
|
||||
return (MouseButton)(rawButton - 1);
|
||||
}
|
||||
|
||||
public void UpdatePosition()
|
||||
public unsafe void UpdatePosition()
|
||||
{
|
||||
_ = SDL_GetMouseState(out int posX, out int posY);
|
||||
float posX = 0;
|
||||
float posY = 0;
|
||||
_ = SDL_GetMouseState(&posX, &posY);
|
||||
Vector2 position = new(posX, posY);
|
||||
|
||||
if (CurrentPosition != position)
|
||||
@@ -76,7 +79,7 @@ namespace Ryujinx.Input.SDL2
|
||||
{
|
||||
if (!_isHidden)
|
||||
{
|
||||
if (SDL_ShowCursor(SDL_DISABLE) != SDL_DISABLE)
|
||||
if (!SDL_HideCursor())
|
||||
{
|
||||
Logger.Error?.PrintMsg(LogClass.Application, "Failed to disable the cursor.");
|
||||
}
|
||||
@@ -88,7 +91,7 @@ namespace Ryujinx.Input.SDL2
|
||||
{
|
||||
if (_isHidden)
|
||||
{
|
||||
if (SDL_ShowCursor(SDL_ENABLE) != SDL_ENABLE)
|
||||
if (!SDL_ShowCursor())
|
||||
{
|
||||
Logger.Error?.PrintMsg(LogClass.Application, "Failed to enable the cursor.");
|
||||
}
|
||||
@@ -100,15 +103,15 @@ namespace Ryujinx.Input.SDL2
|
||||
|
||||
public void Update(SDL_Event evnt)
|
||||
{
|
||||
switch (evnt.type)
|
||||
switch (evnt.Type)
|
||||
{
|
||||
case SDL_EventType.SDL_MOUSEBUTTONDOWN:
|
||||
case SDL_EventType.SDL_MOUSEBUTTONUP:
|
||||
uint rawButton = evnt.button.button;
|
||||
case SDL_EventType.SDL_EVENT_MOUSE_BUTTON_DOWN:
|
||||
case SDL_EventType.SDL_EVENT_MOUSE_BUTTON_UP:
|
||||
uint rawButton = (uint)evnt.button.Button;
|
||||
|
||||
if (rawButton is > 0 and <= ((int)MouseButton.Count))
|
||||
{
|
||||
PressedButtons[(int)DriverButtonToMouseButton(rawButton)] = evnt.type == SDL_EventType.SDL_MOUSEBUTTONDOWN;
|
||||
PressedButtons[(int)DriverButtonToMouseButton(rawButton)] = evnt.Type == SDL_EventType.SDL_EVENT_MOUSE_BUTTON_DOWN;
|
||||
|
||||
CurrentPosition = new Vector2(evnt.button.x, evnt.button.y);
|
||||
}
|
||||
@@ -116,13 +119,13 @@ namespace Ryujinx.Input.SDL2
|
||||
break;
|
||||
|
||||
// NOTE: On Linux using Wayland mouse motion events won't be received at all.
|
||||
case SDL_EventType.SDL_MOUSEMOTION:
|
||||
case SDL_EventType.SDL_EVENT_MOUSE_MOTION:
|
||||
CurrentPosition = new Vector2(evnt.motion.x, evnt.motion.y);
|
||||
_lastCursorMoveTime = Stopwatch.GetTimestamp();
|
||||
|
||||
break;
|
||||
|
||||
case SDL_EventType.SDL_MOUSEWHEEL:
|
||||
case SDL_EventType.SDL_EVENT_MOUSE_WHEEL:
|
||||
Scroll = new Vector2(evnt.wheel.x, evnt.wheel.y);
|
||||
|
||||
break;
|
||||
@@ -144,7 +147,7 @@ namespace Ryujinx.Input.SDL2
|
||||
return ClientSize;
|
||||
}
|
||||
|
||||
public string DriverName => "SDL2";
|
||||
public string DriverName => "SDL3";
|
||||
|
||||
public event Action<string> OnGamepadConnected
|
||||
{
|
||||
@@ -162,7 +165,7 @@ namespace Ryujinx.Input.SDL2
|
||||
|
||||
public IGamepad GetGamepad(string id)
|
||||
{
|
||||
return new SDL2Mouse(this);
|
||||
return new SDL3Mouse(this);
|
||||
}
|
||||
|
||||
public IEnumerable<IGamepad> GetGamepads() => [GetGamepad("0")];
|
||||
@@ -1,17 +1,17 @@
|
||||
using Ryujinx.SDL2.Common;
|
||||
using Ryujinx.SDL3.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Input.SDL2
|
||||
namespace Ryujinx.Input.SDL3
|
||||
{
|
||||
public class SDL2KeyboardDriver : IGamepadDriver
|
||||
public class SDL3KeyboardDriver : IGamepadDriver
|
||||
{
|
||||
public SDL2KeyboardDriver()
|
||||
public SDL3KeyboardDriver()
|
||||
{
|
||||
SDL2Driver.Instance.Initialize();
|
||||
SDL3Driver.Instance.Initialize();
|
||||
}
|
||||
|
||||
public string DriverName => "SDL2";
|
||||
public string DriverName => "SDL3";
|
||||
|
||||
private static readonly string[] _keyboardIdentifers = ["0"];
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace Ryujinx.Input.SDL2
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
SDL2Driver.Instance.Dispose();
|
||||
SDL3Driver.Instance.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ namespace Ryujinx.Input.SDL2
|
||||
return null;
|
||||
}
|
||||
|
||||
return new SDL2Keyboard(this, _keyboardIdentifers[0], "All keyboards");
|
||||
return new SDL3Keyboard(this, _keyboardIdentifers[0], "All keyboards");
|
||||
}
|
||||
|
||||
public IEnumerable<IGamepad> GetGamepads()
|
||||
@@ -5,6 +5,7 @@ using Ryujinx.Common.Configuration.Hid.Controller.Motion;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.HLE.HOS.Services.Hid;
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
@@ -291,7 +292,7 @@ namespace Ryujinx.Input.HLE
|
||||
{
|
||||
if (controllerConfig.Motion.MotionBackend == MotionInputBackendType.GamepadDriver)
|
||||
{
|
||||
if (gamepad.Features.HasFlag(GamepadFeaturesFlag.Motion))
|
||||
if ((gamepad.Features & GamepadFeaturesFlag.Motion) != 0)
|
||||
{
|
||||
Vector3 accelerometer = gamepad.GetMotionData(MotionInputId.Accelerometer);
|
||||
Vector3 gyroscope = gamepad.GetMotionData(MotionInputId.Gyroscope);
|
||||
@@ -531,6 +532,8 @@ namespace Ryujinx.Input.HLE
|
||||
|
||||
hidKeyboard.Modifier |= value << entry.Target;
|
||||
}
|
||||
|
||||
ArrayPool<bool>.Shared.Return(keyboardState.KeysState);
|
||||
|
||||
return hidKeyboard;
|
||||
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Configuration.Hid;
|
||||
using Ryujinx.Common.Configuration.Hid.Controller;
|
||||
using Ryujinx.Common.Configuration.Hid.Keyboard;
|
||||
using Ryujinx.HLE.HOS.Services.Hid;
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
@@ -18,6 +20,7 @@ namespace Ryujinx.Input.HLE
|
||||
{
|
||||
public class NpadManager : IDisposable
|
||||
{
|
||||
private static readonly ObjectPool<List<SixAxisInput>> _hleMotionStatesPool = new (() => new List<SixAxisInput>(NpadDevices.MaxControllers));
|
||||
private readonly CemuHookClient _cemuHookClient;
|
||||
|
||||
private readonly Lock _lock = new();
|
||||
@@ -215,7 +218,7 @@ namespace Ryujinx.Input.HLE
|
||||
lock (_lock)
|
||||
{
|
||||
List<GamepadInput> hleInputStates = [];
|
||||
List<SixAxisInput> hleMotionStates = new(NpadDevices.MaxControllers);
|
||||
List<SixAxisInput> hleMotionStates = _hleMotionStatesPool.Allocate();
|
||||
|
||||
KeyboardInput? hleKeyboardInput = null;
|
||||
|
||||
@@ -317,6 +320,8 @@ namespace Ryujinx.Input.HLE
|
||||
Vector2 position = IMouse.GetScreenPosition(mouseInput.Position, mouse.ClientSize, aspectRatio);
|
||||
|
||||
_device.Hid.Mouse.Update((int)position.X, (int)position.Y, buttons, (int)mouseInput.Scroll.X, (int)mouseInput.Scroll.Y, true);
|
||||
|
||||
ArrayPool<bool>.Shared.Return(mouseInput.ButtonState);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -324,6 +329,8 @@ namespace Ryujinx.Input.HLE
|
||||
}
|
||||
|
||||
_device.TamperMachine.UpdateInput(hleInputStates);
|
||||
|
||||
_hleMotionStatesPool.Release(hleMotionStates);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Buffers;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Ryujinx.Input
|
||||
@@ -28,7 +29,8 @@ namespace Ryujinx.Input
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
static KeyboardStateSnapshot GetStateSnapshot(IKeyboard keyboard)
|
||||
{
|
||||
bool[] keysState = new bool[(int)Key.Count];
|
||||
|
||||
bool[] keysState = ArrayPool<bool>.Shared.Rent((int)Key.Count);
|
||||
|
||||
for (Key key = 0; key < Key.Count; key++)
|
||||
{
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Buffers;
|
||||
using System.Drawing;
|
||||
using System.Numerics;
|
||||
|
||||
@@ -47,7 +48,7 @@ namespace Ryujinx.Input
|
||||
/// <returns>A snaphost of the state of the mouse.</returns>
|
||||
public static MouseStateSnapshot GetMouseStateSnapshot(IMouse mouse)
|
||||
{
|
||||
bool[] buttons = new bool[(int)MouseButton.Count];
|
||||
bool[] buttons = ArrayPool<bool>.Shared.Rent((int)MouseButton.Count);
|
||||
|
||||
mouse.Buttons.CopyTo(buttons, 0);
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace Ryujinx.Input
|
||||
/// </summary>
|
||||
public class KeyboardStateSnapshot
|
||||
{
|
||||
private readonly bool[] _keysState;
|
||||
public readonly bool[] KeysState;
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="KeyboardStateSnapshot"/>.
|
||||
@@ -15,7 +15,7 @@ namespace Ryujinx.Input
|
||||
/// <param name="keysState">The keys state</param>
|
||||
public KeyboardStateSnapshot(bool[] keysState)
|
||||
{
|
||||
_keysState = keysState;
|
||||
KeysState = keysState;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -24,6 +24,6 @@ namespace Ryujinx.Input
|
||||
/// <param name="key">The key</param>
|
||||
/// <returns>True if the given key is pressed</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool IsPressed(Key key) => _keysState[(int)key];
|
||||
public bool IsPressed(Key key) => KeysState[(int)key];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace Ryujinx.Input
|
||||
/// </summary>
|
||||
public class MouseStateSnapshot
|
||||
{
|
||||
private readonly bool[] _buttonState;
|
||||
public readonly bool[] ButtonState;
|
||||
|
||||
/// <summary>
|
||||
/// The position of the mouse cursor
|
||||
@@ -28,7 +28,7 @@ namespace Ryujinx.Input
|
||||
/// <param name="scroll">The scroll delta</param>
|
||||
public MouseStateSnapshot(bool[] buttonState, Vector2 position, Vector2 scroll)
|
||||
{
|
||||
_buttonState = buttonState;
|
||||
ButtonState = buttonState;
|
||||
|
||||
Position = position;
|
||||
Scroll = scroll;
|
||||
@@ -40,6 +40,6 @@ namespace Ryujinx.Input
|
||||
/// <param name="button">The button</param>
|
||||
/// <returns>True if the given button is pressed</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool IsPressed(MouseButton button) => _buttonState[(int)button];
|
||||
public bool IsPressed(MouseButton button) => ButtonState[(int)button];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
@@ -38,7 +39,7 @@ namespace Ryujinx.Memory.Range
|
||||
index = ~index;
|
||||
}
|
||||
|
||||
RangeItem<T> rangeItem = new(item);
|
||||
RangeItem<T> rangeItem = _rangeItemPool.Allocate().Set(item);
|
||||
|
||||
Insert(index, rangeItem);
|
||||
}
|
||||
@@ -144,6 +145,8 @@ namespace Ryujinx.Memory.Range
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void RemoveAt(int index)
|
||||
{
|
||||
_rangeItemPool.Release(Items[index]);
|
||||
|
||||
if (index < Count - 1)
|
||||
{
|
||||
Items[index + 1].Previous = index > 0 ? Items[index - 1] : null;
|
||||
@@ -433,7 +436,7 @@ namespace Ryujinx.Memory.Range
|
||||
return (Items[index], Items[endIndex - 1]);
|
||||
}
|
||||
|
||||
public RangeItem<T>[] FindOverlapsAsArray(ulong address, ulong size)
|
||||
public RangeItem<T>[] FindOverlapsAsArray(ulong address, ulong size, out int length)
|
||||
{
|
||||
(int index, int endIndex) = BinarySearchEdges(address, address + size);
|
||||
|
||||
@@ -441,11 +444,13 @@ namespace Ryujinx.Memory.Range
|
||||
|
||||
if (index < 0)
|
||||
{
|
||||
result = [];
|
||||
result = null;
|
||||
length = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = new RangeItem<T>[endIndex - index];
|
||||
result = ArrayPool<RangeItem<T>>.Shared.Rent(endIndex - index);
|
||||
length = endIndex - index;
|
||||
|
||||
Array.Copy(Items, index, result, 0, endIndex - index);
|
||||
}
|
||||
|
||||
@@ -36,8 +36,6 @@ namespace Ryujinx.Memory.Range
|
||||
public class RangeList<T> : RangeListBase<T> where T : IRange
|
||||
{
|
||||
public readonly ReaderWriterLockSlim Lock = new();
|
||||
|
||||
private readonly Dictionary<ulong, RangeItem<T>> _quickAccess = new(AddressEqualityComparer.Comparer);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new range list.
|
||||
@@ -93,11 +91,6 @@ namespace Ryujinx.Memory.Range
|
||||
Items[index + 1].Previous = rangeItem;
|
||||
}
|
||||
|
||||
foreach (ulong address in Items[index].QuickAccessAddresses)
|
||||
{
|
||||
_quickAccess.Remove(address);
|
||||
}
|
||||
|
||||
Items[index] = rangeItem;
|
||||
|
||||
return true;
|
||||
@@ -142,11 +135,6 @@ namespace Ryujinx.Memory.Range
|
||||
Items[index + 1].Previous = rangeItem;
|
||||
}
|
||||
|
||||
foreach (ulong address in item.QuickAccessAddresses)
|
||||
{
|
||||
_quickAccess.Remove(address);
|
||||
}
|
||||
|
||||
Items[index] = rangeItem;
|
||||
|
||||
return true;
|
||||
@@ -210,11 +198,6 @@ namespace Ryujinx.Memory.Range
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void RemoveAt(int index)
|
||||
{
|
||||
foreach (ulong address in Items[index].QuickAccessAddresses)
|
||||
{
|
||||
_quickAccess.Remove(address);
|
||||
}
|
||||
|
||||
if (index < Count - 1)
|
||||
{
|
||||
Items[index + 1].Previous = index > 0 ? Items[index - 1] : null;
|
||||
@@ -253,15 +236,6 @@ namespace Ryujinx.Memory.Range
|
||||
int startIndex = BinarySearch(startItem.Address);
|
||||
int endIndex = BinarySearch(endItem.Address);
|
||||
|
||||
for (int i = startIndex; i <= endIndex; i++)
|
||||
{
|
||||
_quickAccess.Remove(Items[i].Address);
|
||||
foreach (ulong addr in Items[i].QuickAccessAddresses)
|
||||
{
|
||||
_quickAccess.Remove(addr);
|
||||
}
|
||||
}
|
||||
|
||||
if (endIndex < Count - 1)
|
||||
{
|
||||
Items[endIndex + 1].Previous = startIndex > 0 ? Items[startIndex - 1] : null;
|
||||
@@ -349,11 +323,6 @@ namespace Ryujinx.Memory.Range
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override RangeItem<T> FindOverlapFast(ulong address, ulong size)
|
||||
{
|
||||
if (_quickAccess.TryGetValue(address, out RangeItem<T> quickResult))
|
||||
{
|
||||
return quickResult;
|
||||
}
|
||||
|
||||
int index = BinarySearch(address, address + size);
|
||||
|
||||
if (index < 0)
|
||||
@@ -361,12 +330,6 @@ namespace Ryujinx.Memory.Range
|
||||
return null;
|
||||
}
|
||||
|
||||
if (Items[index].OverlapsWith(address, address + 1))
|
||||
{
|
||||
_quickAccess.Add(address, Items[index]);
|
||||
Items[index].QuickAccessAddresses.Add(address);
|
||||
}
|
||||
|
||||
return Items[index];
|
||||
}
|
||||
|
||||
|
||||
@@ -1,20 +1,42 @@
|
||||
using System.Collections;
|
||||
using Ryujinx.Common;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Ryujinx.Memory.Range
|
||||
{
|
||||
public class RangeItem<TValue>(TValue value) where TValue : IRange
|
||||
public class RangeItem<TValue> where TValue : IRange
|
||||
{
|
||||
public RangeItem<TValue> Next;
|
||||
public RangeItem<TValue> Previous;
|
||||
|
||||
public readonly ulong Address = value.Address;
|
||||
public readonly ulong EndAddress = value.Address + value.Size;
|
||||
public ulong Address;
|
||||
public ulong EndAddress;
|
||||
|
||||
public readonly TValue Value = value;
|
||||
public TValue Value;
|
||||
|
||||
public RangeItem()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public RangeItem(TValue value)
|
||||
{
|
||||
Address = value.Address;
|
||||
EndAddress = value.Address + value.Size;
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public readonly List<ulong> QuickAccessAddresses = [];
|
||||
public RangeItem<TValue> Set(TValue value)
|
||||
{
|
||||
Next = null;
|
||||
Previous = null;
|
||||
Address = value.Address;
|
||||
EndAddress = value.Address + value.Size;
|
||||
Value = value;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool OverlapsWith(ulong address, ulong endAddress)
|
||||
@@ -23,20 +45,9 @@ namespace Ryujinx.Memory.Range
|
||||
}
|
||||
}
|
||||
|
||||
class AddressEqualityComparer : IEqualityComparer<ulong>
|
||||
{
|
||||
public bool Equals(ulong u1, ulong u2)
|
||||
{
|
||||
return u1 == u2;
|
||||
}
|
||||
|
||||
public int GetHashCode(ulong value) => (int)(value << 5);
|
||||
|
||||
public static readonly AddressEqualityComparer Comparer = new();
|
||||
}
|
||||
|
||||
public unsafe abstract class RangeListBase<T> : IEnumerable<T> where T : IRange
|
||||
{
|
||||
protected static readonly ObjectPool<RangeItem<T>> _rangeItemPool = new(() => new RangeItem<T>());
|
||||
private const int BackingInitialSize = 1024;
|
||||
|
||||
protected RangeItem<T>[] Items;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Ryujinx.Memory.Range;
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Memory.Tracking
|
||||
@@ -300,10 +301,10 @@ namespace Ryujinx.Memory.Tracking
|
||||
|
||||
// We use the non-span method here because keeping the lock will cause a deadlock.
|
||||
regions.Lock.EnterReadLock();
|
||||
RangeItem<VirtualRegion>[] overlaps = regions.FindOverlapsAsArray(address, size);
|
||||
RangeItem<VirtualRegion>[] overlaps = regions.FindOverlapsAsArray(address, size, out int length);
|
||||
regions.Lock.ExitReadLock();
|
||||
|
||||
if (overlaps.Length == 0 && !precise)
|
||||
if (length == 0 && !precise)
|
||||
{
|
||||
if (_memoryManager.IsRangeMapped(address, size))
|
||||
{
|
||||
@@ -323,8 +324,8 @@ namespace Ryujinx.Memory.Tracking
|
||||
// Increase the access size to trigger handles with misaligned accesses.
|
||||
size += (ulong)_pageSize;
|
||||
}
|
||||
|
||||
for (int i = 0; i < overlaps.Length; i++)
|
||||
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
VirtualRegion region = overlaps[i].Value;
|
||||
|
||||
@@ -337,6 +338,11 @@ namespace Ryujinx.Memory.Tracking
|
||||
region.Signal(address, size, write, exemptId);
|
||||
}
|
||||
}
|
||||
|
||||
if (length != 0)
|
||||
{
|
||||
ArrayPool<RangeItem<VirtualRegion>>.Shared.Return(overlaps);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Ryujinx.SDL2-CS" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.SDL3-CS" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -5,19 +5,20 @@ using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using static SDL2.SDL;
|
||||
using SDL;
|
||||
using static SDL.SDL3;
|
||||
|
||||
namespace Ryujinx.SDL2.Common
|
||||
namespace Ryujinx.SDL3.Common
|
||||
{
|
||||
public class SDL2Driver : IDisposable
|
||||
public class SDL3Driver : IDisposable
|
||||
{
|
||||
private static SDL2Driver _instance;
|
||||
private static SDL3Driver _instance;
|
||||
|
||||
public static SDL2Driver Instance
|
||||
public static SDL3Driver Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
_instance ??= new SDL2Driver();
|
||||
_instance ??= new SDL3Driver();
|
||||
|
||||
return _instance;
|
||||
}
|
||||
@@ -25,26 +26,22 @@ namespace Ryujinx.SDL2.Common
|
||||
|
||||
public static Action<Action> MainThreadDispatcher { get; set; }
|
||||
|
||||
private const uint SdlInitFlags = SDL_INIT_EVENTS | SDL_INIT_GAMECONTROLLER | SDL_INIT_JOYSTICK | SDL_INIT_AUDIO | SDL_INIT_VIDEO;
|
||||
private const SDL_InitFlags SdlInitFlags = SDL_InitFlags.SDL_INIT_EVENTS | SDL_InitFlags.SDL_INIT_GAMEPAD | SDL_InitFlags.SDL_INIT_JOYSTICK | SDL_InitFlags.SDL_INIT_AUDIO | SDL_InitFlags.SDL_INIT_VIDEO;
|
||||
|
||||
private bool _isRunning;
|
||||
private uint _refereceCount;
|
||||
private Thread _worker;
|
||||
|
||||
private const uint SDL_JOYBATTERYUPDATED = 1543;
|
||||
public event Action<SDL_JoystickID> OnJoyStickConnected;
|
||||
public event Action<SDL_JoystickID> OnJoystickDisconnected;
|
||||
|
||||
public event Action<int, int> OnJoyStickConnected;
|
||||
public event Action<int> OnJoystickDisconnected;
|
||||
public event Action<SDL_JoystickID, SDL_PowerState> OnJoyBatteryUpdated;
|
||||
|
||||
public event Action<int, SDL_JoystickPowerLevel> OnJoyBatteryUpdated;
|
||||
|
||||
private ConcurrentDictionary<uint, Action<SDL_Event>> _registeredWindowHandlers;
|
||||
private ConcurrentDictionary<SDL_WindowID, Action<SDL_Event>> _registeredWindowHandlers;
|
||||
|
||||
private readonly Lock _lock = new();
|
||||
|
||||
private SDL2Driver() { }
|
||||
|
||||
private const string SDL_HINT_JOYSTICK_HIDAPI_COMBINE_JOY_CONS = "SDL_JOYSTICK_HIDAPI_COMBINE_JOY_CONS";
|
||||
private SDL3Driver() { }
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
@@ -58,20 +55,19 @@ namespace Ryujinx.SDL2.Common
|
||||
}
|
||||
|
||||
SDL_SetHint(SDL_HINT_APP_NAME, "Ryujinx");
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, "1");
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1");
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_ENHANCED_REPORTS , "1");
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1");
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_HOME_LED, "0");
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1");
|
||||
SDL_SetHint(SDL_HINT_VIDEO_ALLOW_SCREENSAVER, "1");
|
||||
|
||||
// NOTE: As of SDL2 2.24.0, joycons are combined by default but the motion source only come from one of them.
|
||||
// NOTE: As of SDL3 2.24.0, joycons are combined by default but the motion source only come from one of them.
|
||||
// We disable this behavior for now.
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_COMBINE_JOY_CONS, "0");
|
||||
|
||||
if (SDL_Init(SdlInitFlags) != 0)
|
||||
if (!SDL_Init(SdlInitFlags))
|
||||
{
|
||||
string errorMessage = $"SDL2 initialization failed with error \"{SDL_GetError()}\"";
|
||||
string errorMessage = $"SDL3 initialization failed with error \"{SDL_GetError()}\"";
|
||||
|
||||
Logger.Error?.Print(LogClass.Application, errorMessage);
|
||||
|
||||
@@ -79,78 +75,77 @@ namespace Ryujinx.SDL2.Common
|
||||
}
|
||||
|
||||
// First ensure that we only enable joystick events (for connected/disconnected).
|
||||
if (SDL_GameControllerEventState(SDL_IGNORE) != SDL_IGNORE)
|
||||
SDL_SetGamepadEventsEnabled(false);
|
||||
SDL_SetJoystickEventsEnabled(true);
|
||||
if (SDL_GamepadEventsEnabled())
|
||||
{
|
||||
Logger.Error?.PrintMsg(LogClass.Application, "Couldn't change the state of game controller events.");
|
||||
}
|
||||
|
||||
if (SDL_JoystickEventState(SDL_ENABLE) < 0)
|
||||
if (!SDL_JoystickEventsEnabled())
|
||||
{
|
||||
Logger.Error?.PrintMsg(LogClass.Application, $"Failed to enable joystick event polling: {SDL_GetError()}");
|
||||
}
|
||||
|
||||
// Disable all joysticks information, we don't need them no need to flood the event queue for that.
|
||||
SDL_EventState(SDL_EventType.SDL_JOYAXISMOTION, SDL_DISABLE);
|
||||
SDL_EventState(SDL_EventType.SDL_JOYBALLMOTION, SDL_DISABLE);
|
||||
SDL_EventState(SDL_EventType.SDL_JOYHATMOTION, SDL_DISABLE);
|
||||
SDL_EventState(SDL_EventType.SDL_JOYBUTTONDOWN, SDL_DISABLE);
|
||||
SDL_EventState(SDL_EventType.SDL_JOYBUTTONUP, SDL_DISABLE);
|
||||
SDL_SetEventEnabled((uint)SDL_EventType.SDL_EVENT_JOYSTICK_AXIS_MOTION, false);
|
||||
SDL_SetEventEnabled((uint)SDL_EventType.SDL_EVENT_JOYSTICK_BALL_MOTION, false);
|
||||
SDL_SetEventEnabled((uint)SDL_EventType.SDL_EVENT_JOYSTICK_HAT_MOTION, false);
|
||||
SDL_SetEventEnabled((uint)SDL_EventType.SDL_EVENT_JOYSTICK_BUTTON_DOWN, false);
|
||||
SDL_SetEventEnabled((uint)SDL_EventType.SDL_EVENT_JOYSTICK_BUTTON_UP, false);
|
||||
|
||||
SDL_EventState(SDL_EventType.SDL_CONTROLLERSENSORUPDATE, SDL_DISABLE);
|
||||
SDL_SetEventEnabled((uint)SDL_EventType.SDL_EVENT_GAMEPAD_SENSOR_UPDATE, false);
|
||||
|
||||
string gamepadDbPath = Path.Combine(AppDataManager.BaseDirPath, "SDL_GameControllerDB.txt");
|
||||
|
||||
if (File.Exists(gamepadDbPath))
|
||||
{
|
||||
SDL_GameControllerAddMappingsFromFile(gamepadDbPath);
|
||||
SDL_AddGamepadMappingsFromFile(gamepadDbPath);
|
||||
}
|
||||
|
||||
_registeredWindowHandlers = new ConcurrentDictionary<uint, Action<SDL_Event>>();
|
||||
_registeredWindowHandlers = new ConcurrentDictionary<SDL_WindowID, Action<SDL_Event>>();
|
||||
_worker = new Thread(EventWorker);
|
||||
_isRunning = true;
|
||||
_worker.Start();
|
||||
}
|
||||
}
|
||||
|
||||
public bool RegisterWindow(uint windowId, Action<SDL_Event> windowEventHandler)
|
||||
public bool RegisterWindow(SDL_WindowID windowId, Action<SDL_Event> windowEventHandler)
|
||||
{
|
||||
return _registeredWindowHandlers.TryAdd(windowId, windowEventHandler);
|
||||
}
|
||||
|
||||
public void UnregisterWindow(uint windowId)
|
||||
public void UnregisterWindow(SDL_WindowID windowId)
|
||||
{
|
||||
_registeredWindowHandlers.Remove(windowId, out _);
|
||||
}
|
||||
|
||||
private void HandleSDLEvent(ref SDL_Event evnt)
|
||||
{
|
||||
if (evnt.type == SDL_EventType.SDL_JOYDEVICEADDED)
|
||||
SDL_EventType type = evnt.Type;
|
||||
if (type == SDL_EventType.SDL_EVENT_JOYSTICK_ADDED)
|
||||
{
|
||||
int deviceId = evnt.cbutton.which;
|
||||
|
||||
// SDL2 loves to be inconsistent here by providing the device id instead of the instance id (like on removed event), as such we just grab it and send it inside our system.
|
||||
int instanceId = SDL_JoystickGetDeviceInstanceID(deviceId);
|
||||
|
||||
if (instanceId == -1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
SDL_JoystickID instanceId = evnt.jbutton.which;
|
||||
|
||||
// SDL3 loves to be inconsistent here by providing the device id instead of the instance id (like on removed event), as such we just grab it and send it inside our system.
|
||||
Logger.Debug?.Print(LogClass.Application, $"Added joystick instance id {instanceId}");
|
||||
|
||||
OnJoyStickConnected?.Invoke(deviceId, instanceId);
|
||||
OnJoyStickConnected?.Invoke(instanceId);
|
||||
}
|
||||
else if (evnt.type == SDL_EventType.SDL_JOYDEVICEREMOVED)
|
||||
else if (type == SDL_EventType.SDL_EVENT_JOYSTICK_REMOVED)
|
||||
{
|
||||
Logger.Debug?.Print(LogClass.Application, $"Removed joystick instance id {evnt.cbutton.which}");
|
||||
Logger.Debug?.Print(LogClass.Application, $"Removed joystick instance id {evnt.jbutton.which}");
|
||||
|
||||
OnJoystickDisconnected?.Invoke(evnt.cbutton.which);
|
||||
OnJoystickDisconnected?.Invoke(evnt.jbutton.which);
|
||||
}
|
||||
else if ((uint)evnt.type == SDL_JOYBATTERYUPDATED)
|
||||
else if (type == SDL_EventType.SDL_EVENT_JOYSTICK_BATTERY_UPDATED)
|
||||
{
|
||||
OnJoyBatteryUpdated?.Invoke(evnt.cbutton.which, (SDL_JoystickPowerLevel)evnt.user.code);
|
||||
OnJoyBatteryUpdated?.Invoke(evnt.jbutton.which, evnt.jbattery.state);
|
||||
}
|
||||
else if (evnt.type is SDL_EventType.SDL_WINDOWEVENT or SDL_EventType.SDL_MOUSEBUTTONDOWN or SDL_EventType.SDL_MOUSEBUTTONUP)
|
||||
else if (
|
||||
((uint)type >= (uint)SDL_EventType.SDL_EVENT_WINDOW_FIRST && (uint)type <= (uint)SDL_EventType.SDL_EVENT_WINDOW_LAST) ||
|
||||
type is SDL_EventType.SDL_EVENT_MOUSE_BUTTON_DOWN or SDL_EventType.SDL_EVENT_MOUSE_BUTTON_UP
|
||||
)
|
||||
{
|
||||
if (_registeredWindowHandlers.TryGetValue(evnt.window.windowID, out Action<SDL_Event> handler))
|
||||
{
|
||||
@@ -159,7 +154,7 @@ namespace Ryujinx.SDL2.Common
|
||||
}
|
||||
}
|
||||
|
||||
private void EventWorker()
|
||||
private unsafe void EventWorker()
|
||||
{
|
||||
const int WaitTimeMs = 10;
|
||||
|
||||
@@ -169,7 +164,8 @@ namespace Ryujinx.SDL2.Common
|
||||
{
|
||||
MainThreadDispatcher?.Invoke(() =>
|
||||
{
|
||||
while (SDL_PollEvent(out SDL_Event evnt) != 0)
|
||||
SDL_Event evnt = new();
|
||||
while (SDL_PollEvent(&evnt))
|
||||
{
|
||||
HandleSDLEvent(ref evnt);
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using DiscordRPC;
|
||||
using LibHac.Tools.FsSystem;
|
||||
using Ryujinx.Audio.Backends.SDL2;
|
||||
using Ryujinx.Audio.Backends.SDL3;
|
||||
using Ryujinx.Ava;
|
||||
using Ryujinx.Ava.Systems;
|
||||
using Ryujinx.Ava.Systems.Configuration;
|
||||
@@ -157,7 +157,7 @@ namespace Ryujinx.Headless
|
||||
config = new StandardControllerInputConfig
|
||||
{
|
||||
Version = InputConfig.CurrentVersion,
|
||||
Backend = InputBackendType.GamepadSDL2,
|
||||
Backend = InputBackendType.GamepadSDL3,
|
||||
Id = null,
|
||||
ControllerType = ControllerType.JoyconPair,
|
||||
DeadzoneLeft = 0.1f,
|
||||
@@ -305,8 +305,8 @@ namespace Ryujinx.Headless
|
||||
|
||||
return new VulkanRenderer(
|
||||
api,
|
||||
(instance, vk) => new SurfaceKHR((ulong)(vulkanWindow.CreateWindowSurface(instance.Handle))),
|
||||
vulkanWindow.GetRequiredInstanceExtensions,
|
||||
(instance, vk) => new SurfaceKHR((ulong)vulkanWindow.CreateWindowSurface(instance.Handle)),
|
||||
VulkanWindow.GetRequiredInstanceExtensions,
|
||||
preferredGpuId);
|
||||
}
|
||||
|
||||
@@ -350,7 +350,7 @@ namespace Ryujinx.Headless
|
||||
_accountManager,
|
||||
_userChannelPersistence,
|
||||
renderer.TryMakeThreaded(options.BackendThreading),
|
||||
new SDL2HardwareDeviceDriver(),
|
||||
new SDL3HardwareDeviceDriver(),
|
||||
window
|
||||
)
|
||||
);
|
||||
|
||||
@@ -22,13 +22,14 @@ using Ryujinx.HLE.HOS;
|
||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||
using Ryujinx.Input;
|
||||
using Ryujinx.Input.HLE;
|
||||
using Ryujinx.Input.SDL2;
|
||||
using Ryujinx.SDL2.Common;
|
||||
using Ryujinx.Input.SDL3;
|
||||
using Ryujinx.SDL3.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using SDL;
|
||||
|
||||
namespace Ryujinx.Headless
|
||||
{
|
||||
@@ -61,7 +62,7 @@ namespace Ryujinx.Headless
|
||||
AutoResetEvent invoked = new(false);
|
||||
|
||||
// MacOS must perform SDL polls from the main thread.
|
||||
SDL2Driver.MainThreadDispatcher = action =>
|
||||
SDL3Driver.MainThreadDispatcher = action =>
|
||||
{
|
||||
invoked.Reset();
|
||||
|
||||
@@ -180,7 +181,7 @@ namespace Ryujinx.Headless
|
||||
_accountManager = new AccountManager(_libHacHorizonManager.RyujinxClient, option.UserProfile);
|
||||
_userChannelPersistence = new UserChannelPersistence();
|
||||
|
||||
_inputManager = new InputManager(new SDL2KeyboardDriver(), new SDL2GamepadDriver());
|
||||
_inputManager = new InputManager(new SDL3KeyboardDriver(), new SDL3GamepadDriver());
|
||||
|
||||
GraphicsConfig.EnableShaderCache = !option.DisableShaderCache;
|
||||
|
||||
@@ -396,7 +397,7 @@ namespace Ryujinx.Headless
|
||||
_window = window;
|
||||
|
||||
_window.IsFullscreen = options.IsFullscreen;
|
||||
_window.DisplayId = options.DisplayId;
|
||||
_window.DisplayId = (SDL_DisplayID)options.DisplayId;
|
||||
_window.IsExclusiveFullscreen = options.IsExclusiveFullscreen;
|
||||
_window.ExclusiveFullscreenWidth = options.ExclusiveFullscreenWidth;
|
||||
_window.ExclusiveFullscreenHeight = options.ExclusiveFullscreenHeight;
|
||||
|
||||
@@ -5,15 +5,17 @@ using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Graphics.OpenGL;
|
||||
using Ryujinx.Input.HLE;
|
||||
using System;
|
||||
using static SDL2.SDL;
|
||||
using SDL;
|
||||
using static SDL.SDL3;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Headless
|
||||
{
|
||||
class OpenGLWindow : WindowBase
|
||||
unsafe class OpenGLWindow : WindowBase
|
||||
{
|
||||
private static void CheckResult(int result)
|
||||
private static void CheckResult(bool result)
|
||||
{
|
||||
if (result < 0)
|
||||
if (!result)
|
||||
{
|
||||
throw new InvalidOperationException($"SDL_GL function returned an error: {SDL_GetError()}");
|
||||
}
|
||||
@@ -21,21 +23,21 @@ namespace Ryujinx.Headless
|
||||
|
||||
private static void SetupOpenGLAttributes(bool sharedContext, GraphicsDebugLevel debugLevel)
|
||||
{
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_CONTEXT_MAJOR_VERSION, 3));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_CONTEXT_MINOR_VERSION, 3));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_CONTEXT_PROFILE_MASK, SDL_GLprofile.SDL_GL_CONTEXT_PROFILE_COMPATIBILITY));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_CONTEXT_FLAGS, debugLevel != GraphicsDebugLevel.None ? (int)SDL_GLcontext.SDL_GL_CONTEXT_DEBUG_FLAG : 0));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_SHARE_WITH_CURRENT_CONTEXT, sharedContext ? 1 : 0));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_CONTEXT_MAJOR_VERSION, 4));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_CONTEXT_MINOR_VERSION, 3));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_CONTEXT_PROFILE_MASK, (int)SDL_GLProfile.SDL_GL_CONTEXT_PROFILE_COMPATIBILITY));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_CONTEXT_FLAGS, debugLevel != GraphicsDebugLevel.None ? (int)SDL_GLContextFlag.SDL_GL_CONTEXT_DEBUG_FLAG : 0));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_SHARE_WITH_CURRENT_CONTEXT, sharedContext ? 1 : 0));
|
||||
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_ACCELERATED_VISUAL, 1));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_RED_SIZE, 8));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_GREEN_SIZE, 8));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_BLUE_SIZE, 8));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_ALPHA_SIZE, 8));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_DEPTH_SIZE, 16));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_STENCIL_SIZE, 0));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_DOUBLEBUFFER, 1));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_STEREO, 0));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_ACCELERATED_VISUAL, 1));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_RED_SIZE, 8));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_GREEN_SIZE, 8));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_BLUE_SIZE, 8));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_ALPHA_SIZE, 8));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_DEPTH_SIZE, 16));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_STENCIL_SIZE, 0));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_DOUBLEBUFFER, 1));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_STEREO, 0));
|
||||
}
|
||||
|
||||
private class OpenToolkitBindingsContext : IBindingsContext
|
||||
@@ -46,35 +48,35 @@ namespace Ryujinx.Headless
|
||||
}
|
||||
}
|
||||
|
||||
private class SDL2OpenGLContext : IOpenGLContext
|
||||
private class SDL3OpenGLContext : IOpenGLContext
|
||||
{
|
||||
private readonly nint _context;
|
||||
private readonly nint _window;
|
||||
private readonly SDL_GLContextState* _context;
|
||||
private readonly SDL_Window* _window;
|
||||
private readonly bool _shouldDisposeWindow;
|
||||
|
||||
public SDL2OpenGLContext(nint context, nint window, bool shouldDisposeWindow = true)
|
||||
public SDL3OpenGLContext(SDL_GLContextState* context, SDL_Window* window, bool shouldDisposeWindow = true)
|
||||
{
|
||||
_context = context;
|
||||
_window = window;
|
||||
_shouldDisposeWindow = shouldDisposeWindow;
|
||||
}
|
||||
|
||||
public static SDL2OpenGLContext CreateBackgroundContext(SDL2OpenGLContext sharedContext)
|
||||
public unsafe static SDL3OpenGLContext CreateBackgroundContext(SDL3OpenGLContext sharedContext)
|
||||
{
|
||||
sharedContext.MakeCurrent();
|
||||
|
||||
// Ensure we share our contexts.
|
||||
SetupOpenGLAttributes(true, GraphicsDebugLevel.None);
|
||||
nint windowHandle = SDL_CreateWindow("Ryujinx background context window", 0, 0, 1, 1, SDL_WindowFlags.SDL_WINDOW_OPENGL | SDL_WindowFlags.SDL_WINDOW_HIDDEN);
|
||||
nint context = SDL_GL_CreateContext(windowHandle);
|
||||
SDL_Window* windowHandle = SDL_CreateWindow("Ryujinx background context window", 1, 1, SDL_WindowFlags.SDL_WINDOW_OPENGL | SDL_WindowFlags.SDL_WINDOW_HIDDEN);
|
||||
SDL_GLContextState* context = SDL_GL_CreateContext(windowHandle);
|
||||
|
||||
GL.LoadBindings(new OpenToolkitBindingsContext());
|
||||
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 0));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 0));
|
||||
|
||||
CheckResult(SDL_GL_MakeCurrent(windowHandle, nint.Zero));
|
||||
CheckResult(SDL_GL_MakeCurrent(windowHandle, null));
|
||||
|
||||
return new SDL2OpenGLContext(context, windowHandle);
|
||||
return new SDL3OpenGLContext(context, windowHandle);
|
||||
}
|
||||
|
||||
public void MakeCurrent()
|
||||
@@ -84,9 +86,9 @@ namespace Ryujinx.Headless
|
||||
return;
|
||||
}
|
||||
|
||||
int res = SDL_GL_MakeCurrent(_window, _context);
|
||||
bool res = SDL_GL_MakeCurrent(_window, _context);
|
||||
|
||||
if (res != 0)
|
||||
if (!res)
|
||||
{
|
||||
string errorMessage = $"SDL_GL_CreateContext failed with error \"{SDL_GetError()}\"";
|
||||
|
||||
@@ -96,11 +98,11 @@ namespace Ryujinx.Headless
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasContext() => SDL_GL_GetCurrentContext() != nint.Zero;
|
||||
public bool HasContext() => SDL_GL_GetCurrentContext() != null;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
SDL_GL_DeleteContext(_context);
|
||||
SDL_GL_DestroyContext(_context);
|
||||
|
||||
if (_shouldDisposeWindow)
|
||||
{
|
||||
@@ -109,7 +111,7 @@ namespace Ryujinx.Headless
|
||||
}
|
||||
}
|
||||
|
||||
private SDL2OpenGLContext _openGLContext;
|
||||
private SDL3OpenGLContext _openGLContext;
|
||||
|
||||
public OpenGLWindow(
|
||||
InputManager inputManager,
|
||||
@@ -128,10 +130,10 @@ namespace Ryujinx.Headless
|
||||
{
|
||||
// Ensure to not share this context with other contexts before this point.
|
||||
SetupOpenGLAttributes(false, GlLogLevel);
|
||||
nint context = SDL_GL_CreateContext(WindowHandle);
|
||||
SDL_GLContextState* context = SDL_GL_CreateContext(WindowHandle);
|
||||
CheckResult(SDL_GL_SetSwapInterval(1));
|
||||
|
||||
if (context == nint.Zero)
|
||||
if (context == null)
|
||||
{
|
||||
string errorMessage = $"SDL_GL_CreateContext failed with error \"{SDL_GetError()}\"";
|
||||
|
||||
@@ -141,10 +143,10 @@ namespace Ryujinx.Headless
|
||||
}
|
||||
|
||||
// NOTE: The window handle needs to be disposed by the thread that created it and is handled separately.
|
||||
_openGLContext = new SDL2OpenGLContext(context, WindowHandle, false);
|
||||
_openGLContext = new SDL3OpenGLContext(context, WindowHandle, false);
|
||||
|
||||
// First take exclusivity on the OpenGL context.
|
||||
((OpenGLRenderer)Renderer).InitializeBackgroundContext(SDL2OpenGLContext.CreateBackgroundContext(_openGLContext));
|
||||
((OpenGLRenderer)Renderer).InitializeBackgroundContext(SDL3OpenGLContext.CreateBackgroundContext(_openGLContext));
|
||||
|
||||
_openGLContext.MakeCurrent();
|
||||
|
||||
@@ -160,7 +162,8 @@ namespace Ryujinx.Headless
|
||||
else if (IsFullscreen)
|
||||
{
|
||||
// NOTE: grabbing the main display's dimensions directly as OpenGL doesn't scale along like the VulkanWindow.
|
||||
if (SDL_GetDisplayBounds(DisplayId, out SDL_Rect displayBounds) < 0)
|
||||
SDL_Rect displayBounds = new();
|
||||
if (!SDL_GetDisplayBounds(DisplayId, &displayBounds))
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Application, $"Could not retrieve display bounds: {SDL_GetError()}");
|
||||
|
||||
@@ -189,7 +192,7 @@ namespace Ryujinx.Headless
|
||||
Device.DisposeGpu();
|
||||
|
||||
// Unbind context and destroy everything
|
||||
CheckResult(SDL_GL_MakeCurrent(WindowHandle, nint.Zero));
|
||||
CheckResult(SDL_GL_MakeCurrent(WindowHandle, null));
|
||||
_openGLContext.Dispose();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
using Ryujinx.Common.Configuration;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Input.HLE;
|
||||
using Ryujinx.SDL2.Common;
|
||||
using Ryujinx.SDL3.Common;
|
||||
using System;
|
||||
using SDL;
|
||||
using static SDL.SDL3;
|
||||
using System.Runtime.InteropServices;
|
||||
using static SDL2.SDL;
|
||||
|
||||
namespace Ryujinx.Headless
|
||||
{
|
||||
@@ -39,18 +40,15 @@ namespace Ryujinx.Headless
|
||||
}
|
||||
}
|
||||
|
||||
private static void BasicInvoke(Action action)
|
||||
public unsafe nint CreateWindowSurface(nint instance)
|
||||
{
|
||||
action();
|
||||
}
|
||||
|
||||
public nint CreateWindowSurface(nint instance)
|
||||
{
|
||||
ulong surfaceHandle = 0;
|
||||
VkSurfaceKHR_T surface = new();
|
||||
VkSurfaceKHR_T* surfaceHandle = &surface;
|
||||
VkSurfaceKHR_T** surfaceHandleHandle = &surfaceHandle;
|
||||
|
||||
void CreateSurface()
|
||||
{
|
||||
if (SDL_Vulkan_CreateSurface(WindowHandle, instance, out surfaceHandle) == SDL_bool.SDL_FALSE)
|
||||
if (!SDL_Vulkan_CreateSurface(WindowHandle, (VkInstance_T*)instance, null, surfaceHandleHandle))
|
||||
{
|
||||
string errorMessage = $"SDL_Vulkan_CreateSurface failed with error \"{SDL_GetError()}\"";
|
||||
|
||||
@@ -60,9 +58,9 @@ namespace Ryujinx.Headless
|
||||
}
|
||||
}
|
||||
|
||||
if (SDL2Driver.MainThreadDispatcher != null)
|
||||
if (SDL3Driver.MainThreadDispatcher != null)
|
||||
{
|
||||
SDL2Driver.MainThreadDispatcher(CreateSurface);
|
||||
SDL3Driver.MainThreadDispatcher(CreateSurface);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -72,32 +70,22 @@ namespace Ryujinx.Headless
|
||||
return (nint)surfaceHandle;
|
||||
}
|
||||
|
||||
public unsafe string[] GetRequiredInstanceExtensions()
|
||||
public unsafe static string[] GetRequiredInstanceExtensions()
|
||||
{
|
||||
if (SDL_Vulkan_GetInstanceExtensions(WindowHandle, out uint extensionsCount, nint.Zero) == SDL_bool.SDL_TRUE)
|
||||
{
|
||||
nint[] rawExtensions = new nint[(int)extensionsCount];
|
||||
string[] extensions = new string[(int)extensionsCount];
|
||||
uint extensionCount = 0;
|
||||
byte** extensions = SDL_Vulkan_GetInstanceExtensions(&extensionCount);
|
||||
if (extensionCount == 0) {
|
||||
string errorMessage = $"SDL_Vulkan_GetInstanceExtensions failed with error \"{SDL_GetError()}\"";
|
||||
|
||||
fixed (nint* rawExtensionsPtr = rawExtensions)
|
||||
{
|
||||
if (SDL_Vulkan_GetInstanceExtensions(WindowHandle, out extensionsCount, (nint)rawExtensionsPtr) == SDL_bool.SDL_TRUE)
|
||||
{
|
||||
for (int i = 0; i < extensions.Length; i++)
|
||||
{
|
||||
extensions[i] = Marshal.PtrToStringUTF8(rawExtensions[i]);
|
||||
}
|
||||
Logger.Error?.Print(LogClass.Application, errorMessage);
|
||||
|
||||
return extensions;
|
||||
}
|
||||
}
|
||||
throw new Exception(errorMessage);
|
||||
}
|
||||
|
||||
string errorMessage = $"SDL_Vulkan_GetInstanceExtensions failed with error \"{SDL_GetError()}\"";
|
||||
|
||||
Logger.Error?.Print(LogClass.Application, errorMessage);
|
||||
|
||||
throw new Exception(errorMessage);
|
||||
string[] extensionArr = new string[extensionCount];
|
||||
for (int i = 0; i < extensionCount; i++) {
|
||||
extensionArr[i] = Marshal.PtrToStringUTF8((nint)extensions[i]);
|
||||
}
|
||||
return extensionArr;
|
||||
}
|
||||
|
||||
protected override void FinalizeWindowRenderer()
|
||||
|
||||
@@ -15,37 +15,34 @@ using Ryujinx.HLE.Loaders.Processes;
|
||||
using Ryujinx.HLE.UI;
|
||||
using Ryujinx.Input;
|
||||
using Ryujinx.Input.HLE;
|
||||
using Ryujinx.Input.SDL2;
|
||||
using Ryujinx.SDL2.Common;
|
||||
using Ryujinx.Input.SDL3;
|
||||
using Ryujinx.SDL3.Common;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using static SDL2.SDL;
|
||||
using SDL;
|
||||
using static SDL.SDL3;
|
||||
using AntiAliasing = Ryujinx.Common.Configuration.AntiAliasing;
|
||||
using ScalingFilter = Ryujinx.Common.Configuration.ScalingFilter;
|
||||
using Switch = Ryujinx.HLE.Switch;
|
||||
using UserProfile = Ryujinx.HLE.HOS.Services.Account.Acc.UserProfile;
|
||||
using LibHac.Util;
|
||||
|
||||
namespace Ryujinx.Headless
|
||||
{
|
||||
abstract partial class WindowBase : IHostUIHandler, IDisposable
|
||||
abstract unsafe partial class WindowBase : IHostUIHandler, IDisposable
|
||||
{
|
||||
protected const int DefaultWidth = 1280;
|
||||
protected const int DefaultHeight = 720;
|
||||
private const int TargetFps = 60;
|
||||
private SDL_WindowFlags DefaultFlags = SDL_WindowFlags.SDL_WINDOW_ALLOW_HIGHDPI | SDL_WindowFlags.SDL_WINDOW_RESIZABLE | SDL_WindowFlags.SDL_WINDOW_INPUT_FOCUS | SDL_WindowFlags.SDL_WINDOW_SHOWN;
|
||||
private SDL_WindowFlags DefaultFlags = SDL_WindowFlags.SDL_WINDOW_HIGH_PIXEL_DENSITY | SDL_WindowFlags.SDL_WINDOW_RESIZABLE | SDL_WindowFlags.SDL_WINDOW_INPUT_FOCUS;
|
||||
private SDL_WindowFlags FullscreenFlag = 0;
|
||||
|
||||
private static readonly ConcurrentQueue<Action> _mainThreadActions = new();
|
||||
|
||||
[LibraryImport("SDL2")]
|
||||
// TODO: Remove this as soon as SDL2-CS was updated to expose this method publicly
|
||||
private static partial nint SDL_LoadBMP_RW(nint src, int freesrc);
|
||||
|
||||
public static void QueueMainThreadAction(Action action)
|
||||
{
|
||||
_mainThreadActions.Enqueue(action);
|
||||
@@ -56,12 +53,12 @@ namespace Ryujinx.Headless
|
||||
public Switch Device { get; private set; }
|
||||
public IRenderer Renderer { get; private set; }
|
||||
|
||||
protected nint WindowHandle { get; set; }
|
||||
protected SDL_Window* WindowHandle { get; set; }
|
||||
|
||||
public IHostUITheme HostUITheme { get; }
|
||||
public int Width { get; private set; }
|
||||
public int Height { get; private set; }
|
||||
public int DisplayId { get; set; }
|
||||
public SDL_DisplayID DisplayId { get; set; }
|
||||
public bool IsFullscreen { get; set; }
|
||||
public bool IsExclusiveFullscreen { get; set; }
|
||||
public int ExclusiveFullscreenWidth { get; set; }
|
||||
@@ -70,7 +67,7 @@ namespace Ryujinx.Headless
|
||||
public ScalingFilter ScalingFilter { get; set; }
|
||||
public int ScalingFilterLevel { get; set; }
|
||||
|
||||
protected SDL2MouseDriver MouseDriver;
|
||||
protected SDL3MouseDriver MouseDriver;
|
||||
private readonly InputManager _inputManager;
|
||||
private readonly IKeyboard _keyboardInterface;
|
||||
protected readonly GraphicsDebugLevel GlLogLevel;
|
||||
@@ -83,7 +80,7 @@ namespace Ryujinx.Headless
|
||||
private long _ticks;
|
||||
private bool _isActive;
|
||||
private bool _isStopped;
|
||||
private uint _windowId;
|
||||
private SDL_WindowID _windowId;
|
||||
|
||||
private string _gpuDriverName;
|
||||
|
||||
@@ -99,7 +96,7 @@ namespace Ryujinx.Headless
|
||||
HideCursorMode hideCursorMode,
|
||||
bool ignoreControllerApplet)
|
||||
{
|
||||
MouseDriver = new SDL2MouseDriver(hideCursorMode);
|
||||
MouseDriver = new SDL3MouseDriver(hideCursorMode);
|
||||
_inputManager = inputManager;
|
||||
_inputManager.SetMouseDriver(MouseDriver);
|
||||
NpadManager = _inputManager.CreateNpadManager();
|
||||
@@ -116,7 +113,7 @@ namespace Ryujinx.Headless
|
||||
_ignoreControllerApplet = ignoreControllerApplet;
|
||||
HostUITheme = new HeadlessHostUiTheme();
|
||||
|
||||
SDL2Driver.Instance.Initialize();
|
||||
SDL3Driver.Instance.Initialize();
|
||||
}
|
||||
|
||||
public void Initialize(Switch device, List<InputConfig> inputConfigs, bool enableKeyboard, bool enableMouse)
|
||||
@@ -155,11 +152,11 @@ namespace Ryujinx.Headless
|
||||
{
|
||||
fixed (byte* iconPtr = iconBytes)
|
||||
{
|
||||
nint rwOpsStruct = SDL_RWFromConstMem((nint)iconPtr, iconBytes.Length);
|
||||
nint iconHandle = SDL_LoadBMP_RW(rwOpsStruct, 1);
|
||||
SDL_IOStream* rwOpsStruct = SDL_IOFromConstMem((nint)iconPtr, (nuint)iconBytes.Length);
|
||||
SDL_Surface* iconHandle = SDL_LoadBMP_IO(rwOpsStruct, true);
|
||||
|
||||
SDL_SetWindowIcon(WindowHandle, iconHandle);
|
||||
SDL_FreeSurface(iconHandle);
|
||||
SDL_DestroySurface(iconHandle);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -183,18 +180,27 @@ namespace Ryujinx.Headless
|
||||
Width = ExclusiveFullscreenWidth;
|
||||
Height = ExclusiveFullscreenHeight;
|
||||
|
||||
DefaultFlags = SDL_WindowFlags.SDL_WINDOW_ALLOW_HIGHDPI;
|
||||
DefaultFlags = SDL_WindowFlags.SDL_WINDOW_HIGH_PIXEL_DENSITY;
|
||||
FullscreenFlag = SDL_WindowFlags.SDL_WINDOW_FULLSCREEN;
|
||||
}
|
||||
else if (IsFullscreen)
|
||||
{
|
||||
DefaultFlags = SDL_WindowFlags.SDL_WINDOW_ALLOW_HIGHDPI;
|
||||
FullscreenFlag = SDL_WindowFlags.SDL_WINDOW_FULLSCREEN_DESKTOP;
|
||||
DefaultFlags = SDL_WindowFlags.SDL_WINDOW_HIGH_PIXEL_DENSITY;
|
||||
FullscreenFlag = SDL_WindowFlags.SDL_WINDOW_BORDERLESS;
|
||||
}
|
||||
|
||||
WindowHandle = SDL_CreateWindow($"Ryujinx {Program.Version}{titleNameSection}{titleVersionSection}{titleIdSection}{titleArchSection}", SDL_WINDOWPOS_CENTERED_DISPLAY(DisplayId), SDL_WINDOWPOS_CENTERED_DISPLAY(DisplayId), Width, Height, DefaultFlags | FullscreenFlag | WindowFlags);
|
||||
SDL_PropertiesID props = SDL_CreateProperties();
|
||||
SDL_SetStringProperty(props, SDL_PROP_WINDOW_CREATE_TITLE_STRING, $"Ryujinx {Program.Version}{titleNameSection}{titleVersionSection}{titleIdSection}{titleArchSection}");
|
||||
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_X_NUMBER, SDL_WINDOWPOS_CENTERED_DISPLAY(DisplayId));
|
||||
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_Y_NUMBER, SDL_WINDOWPOS_CENTERED_DISPLAY(DisplayId));
|
||||
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, Width);
|
||||
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, Height);
|
||||
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_FLAGS_NUMBER, (long)(DefaultFlags | FullscreenFlag | WindowFlags));
|
||||
|
||||
if (WindowHandle == nint.Zero)
|
||||
WindowHandle = SDL_CreateWindowWithProperties(props);
|
||||
SDL_DestroyProperties(props);
|
||||
|
||||
if (WindowHandle == null)
|
||||
{
|
||||
string errorMessage = $"SDL_CreateWindow failed with error \"{SDL_GetError()}\"";
|
||||
|
||||
@@ -206,16 +212,16 @@ namespace Ryujinx.Headless
|
||||
SetWindowIcon();
|
||||
|
||||
_windowId = SDL_GetWindowID(WindowHandle);
|
||||
SDL2Driver.Instance.RegisterWindow(_windowId, HandleWindowEvent);
|
||||
SDL3Driver.Instance.RegisterWindow(_windowId, HandleWindowEvent);
|
||||
}
|
||||
|
||||
private void HandleWindowEvent(SDL_Event evnt)
|
||||
{
|
||||
if (evnt.type == SDL_EventType.SDL_WINDOWEVENT)
|
||||
if ((uint)evnt.Type >= (uint)SDL_EventType.SDL_EVENT_WINDOW_FIRST && (uint)evnt.Type <= (uint)SDL_EventType.SDL_EVENT_WINDOW_LAST)
|
||||
{
|
||||
switch (evnt.window.windowEvent)
|
||||
switch (evnt.Type)
|
||||
{
|
||||
case SDL_WindowEventID.SDL_WINDOWEVENT_SIZE_CHANGED:
|
||||
case SDL_EventType.SDL_EVENT_WINDOW_RESIZED:
|
||||
// Unlike on Windows, this event fires on macOS when triggering fullscreen mode.
|
||||
// And promptly crashes the process because `Renderer?.window.SetSize` is undefined.
|
||||
// As we don't need this to fire in either case we can test for fullscreen.
|
||||
@@ -229,7 +235,7 @@ namespace Ryujinx.Headless
|
||||
|
||||
break;
|
||||
|
||||
case SDL_WindowEventID.SDL_WINDOWEVENT_CLOSE:
|
||||
case SDL_EventType.SDL_EVENT_WINDOW_CLOSE_REQUESTED:
|
||||
Exit();
|
||||
break;
|
||||
}
|
||||
@@ -409,7 +415,7 @@ namespace Ryujinx.Headless
|
||||
// Get screen touch position
|
||||
if (!_enableMouse)
|
||||
{
|
||||
hasTouch = TouchScreenManager.Update(true, (_inputManager.MouseDriver as SDL2MouseDriver).IsButtonPressed(MouseButton.Button1), _aspectRatio.ToFloat());
|
||||
hasTouch = TouchScreenManager.Update(true, (_inputManager.MouseDriver as SDL3MouseDriver).IsButtonPressed(MouseButton.Button1), _aspectRatio.ToFloat());
|
||||
}
|
||||
|
||||
if (!hasTouch)
|
||||
@@ -461,7 +467,7 @@ namespace Ryujinx.Headless
|
||||
|
||||
public bool DisplayInputDialog(SoftwareKeyboardUIArgs args, out string userText)
|
||||
{
|
||||
// SDL2 doesn't support input dialogs
|
||||
// SDL3 doesn't support input dialogs
|
||||
userText = "Ryujinx";
|
||||
|
||||
return true;
|
||||
@@ -476,7 +482,7 @@ namespace Ryujinx.Headless
|
||||
|
||||
public bool DisplayCabinetDialog(out string userText)
|
||||
{
|
||||
// SDL2 doesn't support input dialogs
|
||||
// SDL3 doesn't support input dialogs
|
||||
userText = "Ryujinx";
|
||||
|
||||
return true;
|
||||
@@ -515,27 +521,36 @@ namespace Ryujinx.Headless
|
||||
Exit();
|
||||
}
|
||||
|
||||
public bool DisplayErrorAppletDialog(string title, string message, string[] buttonsText, (uint Module, uint Description)? errorCode = null)
|
||||
public unsafe bool DisplayErrorAppletDialog(string title, string message, string[] buttonsText, (uint Module, uint Description)? errorCode = null)
|
||||
{
|
||||
SDL_MessageBoxData data = new()
|
||||
{
|
||||
title = title,
|
||||
message = message,
|
||||
buttons = new SDL_MessageBoxButtonData[buttonsText.Length],
|
||||
numbuttons = buttonsText.Length,
|
||||
window = WindowHandle
|
||||
};
|
||||
SDL_MessageBoxButtonData[] buttons = new SDL_MessageBoxButtonData[buttonsText.Length];
|
||||
|
||||
for (int i = 0; i < buttonsText.Length; i++)
|
||||
{
|
||||
data.buttons[i] = new SDL_MessageBoxButtonData
|
||||
string buttonText = buttonsText[i];
|
||||
fixed (byte* pButtonText = &buttonText.ToBytes()[0])
|
||||
buttons[i] = new SDL_MessageBoxButtonData
|
||||
{
|
||||
buttonid = i,
|
||||
text = buttonsText[i],
|
||||
buttonID = i,
|
||||
text = pButtonText,
|
||||
};
|
||||
}
|
||||
|
||||
SDL_ShowMessageBox(ref data, out int _);
|
||||
fixed (byte* pTitle = &title.ToBytes()[0])
|
||||
fixed (byte* pMessage = &message.ToBytes()[0])
|
||||
fixed (SDL_MessageBoxButtonData* p = &buttons[0]) {
|
||||
SDL_MessageBoxData data = new()
|
||||
{
|
||||
title = pTitle,
|
||||
message = pMessage,
|
||||
buttons = p,
|
||||
numbuttons = buttonsText.Length,
|
||||
window = WindowHandle
|
||||
};
|
||||
|
||||
|
||||
SDL_ShowMessageBox(&data, null);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -553,11 +568,11 @@ namespace Ryujinx.Headless
|
||||
TouchScreenManager?.Dispose();
|
||||
NpadManager.Dispose();
|
||||
|
||||
SDL2Driver.Instance.UnregisterWindow(_windowId);
|
||||
SDL3Driver.Instance.UnregisterWindow(_windowId);
|
||||
|
||||
SDL_DestroyWindow(WindowHandle);
|
||||
|
||||
SDL2Driver.Instance.Dispose();
|
||||
SDL3Driver.Instance.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Common.SystemInterop;
|
||||
using Ryujinx.Graphics.Vulkan.MoltenVK;
|
||||
using Ryujinx.Headless;
|
||||
using Ryujinx.SDL2.Common;
|
||||
using Ryujinx.SDL3.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
@@ -149,8 +149,8 @@ namespace Ryujinx.Ava
|
||||
// Initialize Discord integration.
|
||||
DiscordIntegrationModule.Initialize();
|
||||
|
||||
// Initialize SDL2 driver
|
||||
SDL2Driver.MainThreadDispatcher = action => Dispatcher.UIThread.InvokeAsync(action, DispatcherPriority.Input);
|
||||
// Initialize SDL3 driver
|
||||
SDL3Driver.MainThreadDispatcher = action => Dispatcher.UIThread.InvokeAsync(action, DispatcherPriority.Input);
|
||||
|
||||
ReloadConfig();
|
||||
|
||||
|
||||
@@ -77,10 +77,10 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ryujinx.Audio.Backends.SDL2\Ryujinx.Audio.Backends.SDL2.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Audio.Backends.SDL3\Ryujinx.Audio.Backends.SDL3.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Graphics.Vulkan\Ryujinx.Graphics.Vulkan.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Input\Ryujinx.Input.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Input.SDL2\Ryujinx.Input.SDL2.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Input.SDL3\Ryujinx.Input.SDL3.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Audio.Backends.OpenAL\Ryujinx.Audio.Backends.OpenAL.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Audio.Backends.SoundIo\Ryujinx.Audio.Backends.SoundIo.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
||||
|
||||
@@ -8,7 +8,7 @@ using LibHac.Common;
|
||||
using LibHac.Ns;
|
||||
using Ryujinx.Audio.Backends.Dummy;
|
||||
using Ryujinx.Audio.Backends.OpenAL;
|
||||
using Ryujinx.Audio.Backends.SDL2;
|
||||
using Ryujinx.Audio.Backends.SDL3;
|
||||
using Ryujinx.Audio.Backends.SoundIo;
|
||||
using Ryujinx.Audio.Integration;
|
||||
using Ryujinx.Ava.Common;
|
||||
@@ -949,7 +949,7 @@ namespace Ryujinx.Ava.Systems
|
||||
{
|
||||
List<AudioBackend> availableBackends =
|
||||
[
|
||||
AudioBackend.SDL2,
|
||||
AudioBackend.SDL3,
|
||||
AudioBackend.SoundIo,
|
||||
AudioBackend.OpenAl,
|
||||
AudioBackend.Dummy
|
||||
@@ -957,6 +957,9 @@ namespace Ryujinx.Ava.Systems
|
||||
|
||||
AudioBackend preferredBackend = ConfigurationState.Instance.System.AudioBackend.Value;
|
||||
|
||||
if (preferredBackend is AudioBackend.SDL2)
|
||||
preferredBackend = AudioBackend.SDL3;
|
||||
|
||||
for (int i = 0; i < availableBackends.Count; i++)
|
||||
{
|
||||
if (availableBackends[i] == preferredBackend)
|
||||
@@ -988,7 +991,7 @@ namespace Ryujinx.Ava.Systems
|
||||
|
||||
deviceDriver = currentBackend switch
|
||||
{
|
||||
AudioBackend.SDL2 => InitializeAudioBackend<SDL2HardwareDeviceDriver>(AudioBackend.SDL2, nextBackend),
|
||||
AudioBackend.SDL3 => InitializeAudioBackend<SDL3HardwareDeviceDriver>(AudioBackend.SDL3, nextBackend),
|
||||
AudioBackend.SoundIo => InitializeAudioBackend<SoundIoHardwareDeviceDriver>(AudioBackend.SoundIo, nextBackend),
|
||||
AudioBackend.OpenAl => InitializeAudioBackend<OpenALHardwareDeviceDriver>(AudioBackend.OpenAl, nextBackend),
|
||||
_ => new DummyHardwareDeviceDriver(),
|
||||
|
||||
@@ -9,6 +9,7 @@ namespace Ryujinx.Ava.Systems.Configuration
|
||||
Dummy,
|
||||
OpenAl,
|
||||
SoundIo,
|
||||
SDL2,
|
||||
SDL3,
|
||||
SDL2 = SDL3
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace Ryujinx.Ava.Systems.Configuration
|
||||
/// <summary>
|
||||
/// The current version of the file format
|
||||
/// </summary>
|
||||
public const int CurrentVersion = 70;
|
||||
public const int CurrentVersion = 71;
|
||||
|
||||
/// <summary>
|
||||
/// Version of the configuration file format
|
||||
|
||||
@@ -484,7 +484,13 @@ namespace Ryujinx.Ava.Systems.Configuration
|
||||
};
|
||||
}
|
||||
),
|
||||
(69, static cff => cff.SkipUserProfiles = false)
|
||||
(69, static cff => cff.SkipUserProfiles = false),
|
||||
// no migration needed for 70
|
||||
(71, static cff =>
|
||||
{
|
||||
if (cff.AudioBackend is AudioBackend.SDL2)
|
||||
cff.AudioBackend = AudioBackend.SDL3;
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,7 +207,7 @@ namespace Ryujinx.Ava.Systems.Configuration
|
||||
System.EnableInternetAccess.Value = false;
|
||||
System.EnableFsIntegrityChecks.Value = true;
|
||||
System.FsGlobalAccessLogMode.Value = 0;
|
||||
System.AudioBackend.Value = AudioBackend.SDL2;
|
||||
System.AudioBackend.Value = AudioBackend.SDL3;
|
||||
System.AudioVolume.Value = 1;
|
||||
System.MemoryManagerMode.Value = MemoryManagerMode.HostMappedUnsafe;
|
||||
System.DramSize.Value = MemoryConfiguration.MemoryConfiguration4GiB;
|
||||
|
||||
@@ -104,7 +104,7 @@ namespace Ryujinx.Ava.Systems
|
||||
? $"Canary {currentVersion} → Canary {newVersion}"
|
||||
: $"{currentVersion} → {newVersion}";
|
||||
|
||||
Logger.Info?.Print(LogClass.Application, $"Version found: {newVersionString}");
|
||||
Logger.Info?.Print(LogClass.Application, $"Version found: {newVersionString.Replace("→", "->")}");
|
||||
|
||||
RequestUserToUpdate:
|
||||
// Show a message asking the user if they want to update
|
||||
|
||||
@@ -18,6 +18,12 @@ namespace Ryujinx.Ava.UI.Applet
|
||||
{
|
||||
public partial class ProfileSelectorDialog : RyujinxControl<ProfileSelectorDialogViewModel>
|
||||
{
|
||||
//Fix compiler warning
|
||||
public ProfileSelectorDialog()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public ProfileSelectorDialog(ProfileSelectorDialogViewModel viewModel)
|
||||
{
|
||||
DataContext = ViewModel = viewModel;
|
||||
|
||||
@@ -205,7 +205,7 @@ namespace Ryujinx.Ava.UI.Models.Input
|
||||
{
|
||||
Id = Id,
|
||||
Name = Name,
|
||||
Backend = InputBackendType.GamepadSDL2,
|
||||
Backend = InputBackendType.GamepadSDL3,
|
||||
PlayerIndex = PlayerIndex,
|
||||
ControllerType = ControllerType,
|
||||
LeftJoycon = new LeftJoyconCommonConfig<GamepadInputId>
|
||||
|
||||
@@ -95,7 +95,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||
public bool IsRight { get; set; }
|
||||
public bool IsLeft { get; set; }
|
||||
public string RevertDeviceId { get; set; }
|
||||
public bool HasLed => SelectedGamepad.Features.HasFlag(GamepadFeaturesFlag.Led);
|
||||
public bool HasLed => (SelectedGamepad.Features & GamepadFeaturesFlag.Led) != 0;
|
||||
public bool CanClearLed => SelectedGamepad.Name.ContainsIgnoreCase("DualSense");
|
||||
|
||||
public event Action NotifyChangesEvent;
|
||||
@@ -714,7 +714,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||
config = new StandardControllerInputConfig
|
||||
{
|
||||
Version = InputConfig.CurrentVersion,
|
||||
Backend = InputBackendType.GamepadSDL2,
|
||||
Backend = InputBackendType.GamepadSDL3,
|
||||
Id = id,
|
||||
Name = name,
|
||||
ControllerType = ControllerType.ProController,
|
||||
|
||||
@@ -6,7 +6,7 @@ using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using LibHac.Tools.FsSystem;
|
||||
using Ryujinx.Audio.Backends.OpenAL;
|
||||
using Ryujinx.Audio.Backends.SDL2;
|
||||
using Ryujinx.Audio.Backends.SDL3;
|
||||
using Ryujinx.Audio.Backends.SoundIo;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.Systems.Configuration;
|
||||
@@ -269,7 +269,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
public bool EnableDebug { get; set; }
|
||||
public bool IsOpenAlEnabled { get; set; }
|
||||
public bool IsSoundIoEnabled { get; set; }
|
||||
public bool IsSDL2Enabled { get; set; }
|
||||
public bool IsSDL3Enabled { get; set; }
|
||||
public bool IsCustomResolutionScaleActive => _resolutionScale == 4;
|
||||
public bool IsScalingFilterActive => _scalingFilter == (int)Ryujinx.Common.Configuration.ScalingFilter.Fsr;
|
||||
|
||||
@@ -512,13 +512,13 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
{
|
||||
IsOpenAlEnabled = OpenALHardwareDeviceDriver.IsSupported;
|
||||
IsSoundIoEnabled = SoundIoHardwareDeviceDriver.IsSupported;
|
||||
IsSDL2Enabled = SDL2HardwareDeviceDriver.IsSupported;
|
||||
IsSDL3Enabled = SDL3HardwareDeviceDriver.IsSupported;
|
||||
|
||||
await Dispatcher.UIThread.InvokeAsync(() =>
|
||||
{
|
||||
OnPropertyChanged(nameof(IsOpenAlEnabled));
|
||||
OnPropertyChanged(nameof(IsSoundIoEnabled));
|
||||
OnPropertyChanged(nameof(IsSDL2Enabled));
|
||||
OnPropertyChanged(nameof(IsSDL3Enabled));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,12 @@ namespace Ryujinx.UI.Views.Input
|
||||
{
|
||||
public partial class LedInputView : RyujinxControl<LedInputViewModel>
|
||||
{
|
||||
//Fix compiler warning
|
||||
public LedInputView()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public LedInputView(ControllerInputViewModel viewModel)
|
||||
{
|
||||
ViewModel = new LedInputViewModel
|
||||
|
||||
@@ -44,8 +44,8 @@
|
||||
IsEnabled="{Binding IsSoundIoEnabled}"
|
||||
Content="{ext:Locale SettingsTabSystemAudioBackendSoundIO}" />
|
||||
<ComboBoxItem
|
||||
IsEnabled="{Binding IsSDL2Enabled}"
|
||||
Content="{ext:Locale SettingsTabSystemAudioBackendSDL2}" />
|
||||
IsEnabled="{Binding IsSDL3Enabled}"
|
||||
Content="{ext:Locale SettingsTabSystemAudioBackendSDL3}" />
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
|
||||
|
||||
@@ -11,6 +11,12 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
{
|
||||
internal readonly SettingsViewModel ViewModel;
|
||||
|
||||
//Fix compiler warning
|
||||
public GameSpecificSettingsWindow()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public GameSpecificSettingsWindow(MainWindowViewModel viewModel, bool findUserConfigDir = true)
|
||||
{
|
||||
Title = string.Format(LocaleManager.Instance[LocaleKeys.SettingsWithInfo], viewModel.SelectedApplication.Name, viewModel.SelectedApplication.IdString);
|
||||
|
||||
@@ -29,7 +29,7 @@ using Ryujinx.HLE.FileSystem;
|
||||
using Ryujinx.HLE.HOS;
|
||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||
using Ryujinx.Input.HLE;
|
||||
using Ryujinx.Input.SDL2;
|
||||
using Ryujinx.Input.SDL3;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -105,7 +105,7 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
|
||||
if (Program.PreviewerDetached)
|
||||
{
|
||||
InputManager = new InputManager(new AvaloniaKeyboardDriver(this), new SDL2GamepadDriver());
|
||||
InputManager = new InputManager(new AvaloniaKeyboardDriver(this), new SDL3GamepadDriver());
|
||||
|
||||
_ = this.GetObservable(IsActiveProperty).Subscribe(it => ViewModel.IsActive = it);
|
||||
this.ScalingChanged += OnScalingChanged;
|
||||
|
||||
Reference in New Issue
Block a user