diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d4292162a..69e4dc216 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,7 +12,8 @@ env: RYUJINX_BASE_VERSION: "1.2" RYUJINX_TARGET_RELEASE_CHANNEL_NAME: "release" RYUJINX_TARGET_RELEASE_CHANNEL_OWNER: "Ryubing" - RYUJINX_TARGET_RELEASE_CHANNEL_REPO: "Ryujinx" + RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO: "Ryujinx" + RYUJINX_TARGET_RELEASE_CHANNEL_REPO: "Stable-Releases" RELEASE: 1 jobs: @@ -33,7 +34,7 @@ jobs: script: | github.rest.git.createRef({ owner: "${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}", - repo: "${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}", + repo: "${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO }}", ref: 'refs/tags/${{ steps.version_info.outputs.build_version }}', sha: context.sha }) @@ -52,7 +53,7 @@ jobs: | Linux ARM 64-bit | [Stable Linux ARM Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_arm64.tar.gz) | | macOS | [Stable macOS Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz) | - **Full Changelog**: https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/compare/${{ steps.version_info.outputs.prev_build_version }}...${{ steps.version_info.outputs.build_version }} + **Full Changelog**: https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO }}/compare/${{ steps.version_info.outputs.prev_build_version }}...${{ steps.version_info.outputs.build_version }} omitBodyDuringUpdate: true owner: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }} repo: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }} @@ -92,7 +93,7 @@ jobs: sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_NAME\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_NAME }}/g;' src/Ryujinx.Common/ReleaseInformation.cs sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/g;' src/Ryujinx.Common/ReleaseInformation.cs sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs - sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs + sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs sed -r --in-place 's/\%\%RYUJINX_CONFIG_FILE_NAME\%\%/Config\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs shell: bash @@ -173,7 +174,7 @@ jobs: | Linux ARM 64-bit | [Stable Linux ARM Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_arm64.tar.gz) | | macOS | [Stable macOS Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz) | - **Full Changelog**: https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/compare/${{ steps.version_info.outputs.prev_build_version }}...${{ steps.version_info.outputs.build_version }} + **Full Changelog**: https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO }}/compare/${{ steps.version_info.outputs.prev_build_version }}...${{ steps.version_info.outputs.build_version }} omitBodyDuringUpdate: true allowUpdates: true replacesArtifacts: true @@ -222,7 +223,7 @@ jobs: sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_NAME\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_NAME }}/g;' src/Ryujinx.Common/ReleaseInformation.cs sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/g;' src/Ryujinx.Common/ReleaseInformation.cs sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs - sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs + sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs sed -r --in-place 's/\%\%RYUJINX_CONFIG_FILE_NAME\%\%/Config\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs shell: bash diff --git a/Directory.Packages.props b/Directory.Packages.props index 64867fc5b..62a642374 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -46,7 +46,6 @@ - diff --git a/README.md b/README.md index bb51dee13..1948052ba 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,14 @@
- Ryujinx + Ryujinx # Ryujinx [![Release workflow](https://github.com/Ryubing/Ryujinx/actions/workflows/release.yml/badge.svg)](https://github.com/Ryubing/Ryujinx/actions/workflows/release.yml) -[![Latest release](https://img.shields.io/github/v/release/GreemDev/Ryujinx)](https://github.com/Ryubing/Ryujinx/releases/latest) +[![Latest release](https://img.shields.io/github/v/release/Ryubing/Ryujinx)](https://github.com/Ryubing/Ryujinx/releases/latest)
[![Canary workflow](https://github.com/Ryubing/Ryujinx/actions/workflows/canary.yml/badge.svg)](https://github.com/Ryubing/Ryujinx/actions/workflows/canary.yml) [![Latest canary release](https://img.shields.io/github/v/release/Ryubing/Canary-Releases?label=canary)](https://github.com/Ryubing/Canary-Releases/releases/latest) @@ -97,7 +97,7 @@ If you are planning to contribute or just want to learn more about this project - **Input** - We currently have support for keyboard, mouse, touch input, JoyCon input support, and nearly all controllers. + We currently have support for keyboard, mouse, touch input, Joy-Con input support, and nearly all controllers. Motion controls are natively supported in most cases; for dual-JoyCon motion support, DS4Windows or BetterJoy are currently required. In all scenarios, you can set up everything inside the input configuration menu. diff --git a/Ryujinx.sln b/Ryujinx.sln index 9e197e85f..9ed282d09 100644 --- a/Ryujinx.sln +++ b/Ryujinx.sln @@ -77,15 +77,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Kernel.Gene EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.HLE.Generators", "src\Ryujinx.HLE.Generators\Ryujinx.HLE.Generators.csproj", "{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.Graphics.Metal", "src\Ryujinx.Graphics.Metal\Ryujinx.Graphics.Metal.csproj", "{C08931FA-1191-417A-864F-3882D93E683B}" - ProjectSection(ProjectDependencies) = postProject - {A602AE97-91A5-4608-8DF1-EBF4ED7A0B9E} = {A602AE97-91A5-4608-8DF1-EBF4ED7A0B9E} - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.BuildValidationTasks", "src\Ryujinx.BuildValidationTasks\Ryujinx.BuildValidationTasks.csproj", "{4A89A234-4F19-497D-A576-DDE8CDFC5B22}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.Graphics.Metal.SharpMetalExtensions", "src/Ryujinx.Graphics.Metal.SharpMetalExtensions\Ryujinx.Graphics.Metal.SharpMetalExtensions.csproj", "{81EA598C-DBA1-40B0-8DA4-4796B78F2037}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{36F870C1-3E5F-485F-B426-F0645AF78751}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig @@ -95,6 +86,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution .github\workflows\release.yml = .github\workflows\release.yml EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.BuildValidationTasks", "src\Ryujinx.BuildValidationTasks\Ryujinx.BuildValidationTasks.csproj", "{4A89A234-4F19-497D-A576-DDE8CDFC5B22}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -217,6 +210,10 @@ Global {7C1B2721-13DA-4B62-B046-C626605ECCE6}.Debug|Any CPU.Build.0 = Debug|Any CPU {7C1B2721-13DA-4B62-B046-C626605ECCE6}.Release|Any CPU.ActiveCfg = Release|Any CPU {7C1B2721-13DA-4B62-B046-C626605ECCE6}.Release|Any CPU.Build.0 = Release|Any CPU + {BA161CA0-CD65-4E6E-B644-51C8D1E542DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BA161CA0-CD65-4E6E-B644-51C8D1E542DC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BA161CA0-CD65-4E6E-B644-51C8D1E542DC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BA161CA0-CD65-4E6E-B644-51C8D1E542DC}.Release|Any CPU.Build.0 = Release|Any CPU {6AE2A5E8-4C5A-48B9-997B-E1455C0355C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6AE2A5E8-4C5A-48B9-997B-E1455C0355C6}.Debug|Any CPU.Build.0 = Debug|Any CPU {6AE2A5E8-4C5A-48B9-997B-E1455C0355C6}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -249,16 +246,9 @@ Global {B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Debug|Any CPU.Build.0 = Debug|Any CPU {B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Release|Any CPU.ActiveCfg = Release|Any CPU {B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Release|Any CPU.Build.0 = Release|Any CPU - {C08931FA-1191-417A-864F-3882D93E683B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C08931FA-1191-417A-864F-3882D93E683B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C08931FA-1191-417A-864F-3882D93E683B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C08931FA-1191-417A-864F-3882D93E683B}.Release|Any CPU.Build.0 = Release|Any CPU {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}.Release|Any CPU.ActiveCfg = Release|Any CPU - {81EA598C-DBA1-40B0-8DA4-4796B78F2037}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {81EA598C-DBA1-40B0-8DA4-4796B78F2037}.Debug|Any CPU.Build.0 = Debug|Any CPU - {81EA598C-DBA1-40B0-8DA4-4796B78F2037}.Release|Any CPU.ActiveCfg = Release|Any CPU - {81EA598C-DBA1-40B0-8DA4-4796B78F2037}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/docs/compatibility.csv b/docs/compatibility.csv index 5f6ead1eb..7b3f814c5 100644 --- a/docs/compatibility.csv +++ b/docs/compatibility.csv @@ -631,6 +631,7 @@ 010030D012FF6000,"Bus Driver Simulator",,playable,2022-10-17 13:55:27 0100A9101418C000,"BUSTAFELLOWS",nvdec,playable,2020-10-17 20:04:41 0100177005C8A000,"BUTCHER",,playable,2021-01-11 18:50:17 +01008c2019598000,"Bluey: The Videogame",,playable,2025-02-11 04:38:00 01000B900D8B0000,"Cadence of Hyrule: Crypt of the NecroDancer Featuring The Legend of Zelda",slow;nvdec,playable,2024-04-01 22:43:40 010065700EE06000,"Cadence of Hyrule: Crypt of the NecroDancer Featuring The Legend of Zelda Demo",demo;gpu;nvdec,ingame,2021-02-14 21:48:15 01005C00117A8000,"Café Enchanté",,playable,2020-11-13 14:54:25 @@ -1382,6 +1383,9 @@ 0100763015C2E000,"Gunvolt Chronicles: Luminous Avenger iX 2",crash;Needs Update,nothing,2022-04-29 15:34:34 01002C8018554000,"Gurimugurimoa OnceMore Demo",,playable,2022-07-29 22:07:31 0100AC601DCA8000,"GYLT",crash,ingame,2024-03-18 20:16:51 +0100c3c012718000,"Grand Theft Auto: III – The Definitive Edition",gpu;UE4,ingame,2022-10-31 20:13:52 +0100182014022000,"Grand Theft Auto: Vice City – The Definitive Edition",gpu;UE4,ingame,2022-10-31 20:13:52 +010065a014024000,"Grand Theft Auto: San Andreas – The Definitive Edition",gpu;UE4,ingame,2022-10-31 20:13:52 0100822012D76000,"HAAK",gpu,ingame,2023-02-19 14:31:05 01007E100EFA8000,"Habroxia",,playable,2020-06-16 23:04:42 0100535012974000,"Hades",vulkan,playable,2022-10-05 10:45:21 @@ -1436,7 +1440,7 @@ 010083A018262000,"Hitman: Blood Money — Reprisal",deadlock,ingame,2024-09-28 16:28:50 01004B100A5CC000,"Hob: The Definitive Edition",,playable,2021-01-13 09:39:19 0100F7300ED2C000,"Hoggy2",,playable,2022-10-10 13:53:35 -0100F7E00C70E000,"Hogwarts Legacy",slow,ingame,2024-09-03 19:53:58 +0100F7E00C70E000,"Hogwarts Legacy",UE4;slow,ingame,2024-09-03 19:53:58 0100633007D48000,"Hollow Knight",nvdec,playable,2023-01-16 15:44:56 0100F2100061E800,"Hollow0",UE4;gpu,ingame,2021-03-03 23:42:56 0100342009E16000,"Holy Potatoes! What The Hell?!",,playable,2020-07-03 10:48:56 @@ -2729,7 +2733,7 @@ 0100C2500FC20000,"Splatoon™ 3",ldn-works;opengl-backend-bug;LAN;amd-vendor-bug,playable,2024-08-04 23:49:11 0100BA0018500000,"Splatoon™ 3: Splatfest World Premiere",gpu;online-broken;demo,ingame,2022-09-19 03:17:12 010062800D39C000,"SpongeBob SquarePants: Battle for Bikini Bottom - Rehydrated",online-broken;UE4;ldn-broken;vulkan-backend-bug,playable,2023-08-01 19:29:34 -01009FB0172F4000,"SpongeBob SquarePants: The Cosmic Shake",gpu;UE4,ingame,2023-08-01 19:29:53 +01009FB0172F4000,"SpongeBob SquarePants: The Cosmic Shake",gpu;UE4,ingame,2024-03-04 16:35:00 010097C01336A000,"Spooky Chase",,playable,2022-11-04 12:17:44 0100C6100D75E000,"Spooky Ghosts Dot Com",,playable,2021-06-15 15:16:11 0100DE9005170000,"Sports Party",nvdec,playable,2021-03-05 13:40:42 diff --git a/src/ARMeilleure/Translation/Cache/JitCache.cs b/src/ARMeilleure/Translation/Cache/JitCache.cs index 7b5f2ca81..e480985b1 100644 --- a/src/ARMeilleure/Translation/Cache/JitCache.cs +++ b/src/ARMeilleure/Translation/Cache/JitCache.cs @@ -24,7 +24,7 @@ namespace ARMeilleure.Translation.Cache private static JitCacheInvalidation _jitCacheInvalidator; - private static CacheMemoryAllocator _cacheAllocator; + private static List _cacheAllocators = []; private static readonly List _cacheEntries = []; @@ -40,37 +40,48 @@ namespace ARMeilleure.Translation.Cache public static void Initialize(IJitMemoryAllocator allocator) { - if (_initialized) - { - return; - } - lock (_lock) { if (_initialized) { - return; + if (OperatingSystem.IsWindows()) + { + JitUnwindWindows.RemoveFunctionTableHandler( + _jitRegions[0].Pointer); + } + + for (int i = 0; i < _jitRegions.Count; i++) + { + _jitRegions[i].Dispose(); + } + + _jitRegions.Clear(); + _cacheAllocators.Clear(); } + else + { + _initialized = true; + } + + _activeRegionIndex = 0; ReservedRegion firstRegion = new(allocator, CacheSize); _jitRegions.Add(firstRegion); - _activeRegionIndex = 0; + + CacheMemoryAllocator firstCacheAllocator = new(CacheSize); + _cacheAllocators.Add(firstCacheAllocator); if (!OperatingSystem.IsWindows() && !OperatingSystem.IsMacOS()) { _jitCacheInvalidator = new JitCacheInvalidation(allocator); } - _cacheAllocator = new CacheMemoryAllocator(CacheSize); - if (OperatingSystem.IsWindows()) { JitUnwindWindows.InstallFunctionTableHandler( firstRegion.Pointer, CacheSize, firstRegion.Pointer + Allocate(_pageSize) ); } - - _initialized = true; } } @@ -136,7 +147,7 @@ namespace ARMeilleure.Translation.Cache if (TryFind(funcOffset, out CacheEntry entry, out int entryIndex) && entry.Offset == funcOffset) { - _cacheAllocator.Free(funcOffset, AlignCodeSize(entry.Size)); + _cacheAllocators[_activeRegionIndex].Free(funcOffset, AlignCodeSize(entry.Size)); _cacheEntries.RemoveAt(entryIndex); } @@ -167,30 +178,24 @@ namespace ARMeilleure.Translation.Cache { codeSize = AlignCodeSize(codeSize); - for (int i = _activeRegionIndex; i < _jitRegions.Count; i++) + int allocOffset = _cacheAllocators[_activeRegionIndex].Allocate(codeSize); + + if (allocOffset >= 0) { - int allocOffset = _cacheAllocator.Allocate(codeSize); - - if (allocOffset >= 0) - { - _jitRegions[i].ExpandIfNeeded((ulong)allocOffset + (ulong)codeSize); - _activeRegionIndex = i; - return allocOffset; - } + _jitRegions[_activeRegionIndex].ExpandIfNeeded((ulong)allocOffset + (ulong)codeSize); + return allocOffset; } int exhaustedRegion = _activeRegionIndex; ReservedRegion newRegion = new(_jitRegions[0].Allocator, CacheSize); _jitRegions.Add(newRegion); _activeRegionIndex = _jitRegions.Count - 1; - - int newRegionNumber = _activeRegionIndex; - Logger.Warning?.Print(LogClass.Cpu, $"JIT Cache Region {exhaustedRegion} exhausted, creating new Cache Region {newRegionNumber} ({((long)(newRegionNumber + 1) * CacheSize).Bytes()} Total Allocation)."); - - _cacheAllocator = new CacheMemoryAllocator(CacheSize); + Logger.Warning?.Print(LogClass.Cpu, $"JIT Cache Region {exhaustedRegion} exhausted, creating new Cache Region {_activeRegionIndex} ({((long)(_activeRegionIndex + 1) * CacheSize).Bytes()} Total Allocation)."); - int allocOffsetNew = _cacheAllocator.Allocate(codeSize); + _cacheAllocators.Add(new CacheMemoryAllocator(CacheSize)); + + int allocOffsetNew = _cacheAllocators[_activeRegionIndex].Allocate(codeSize); if (allocOffsetNew < 0) { throw new OutOfMemoryException("Failed to allocate in new Cache Region!"); diff --git a/src/ARMeilleure/Translation/Cache/JitUnwindWindows.cs b/src/ARMeilleure/Translation/Cache/JitUnwindWindows.cs index 01b2aa8ed..15a1051fa 100644 --- a/src/ARMeilleure/Translation/Cache/JitUnwindWindows.cs +++ b/src/ARMeilleure/Translation/Cache/JitUnwindWindows.cs @@ -52,6 +52,11 @@ namespace ARMeilleure.Translation.Cache nint context, [MarshalAs(UnmanagedType.LPWStr)] string outOfProcessCallbackDll); + [LibraryImport("kernel32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + private static unsafe partial bool RtlDeleteFunctionTable( + ulong tableIdentifier); + private static GetRuntimeFunctionCallback _getRuntimeFunctionCallback; private static int _sizeOfRuntimeFunction; @@ -91,6 +96,23 @@ namespace ARMeilleure.Translation.Cache } } + public static void RemoveFunctionTableHandler(nint codeCachePointer) + { + ulong codeCachePtr = (ulong)codeCachePointer.ToInt64(); + + bool result; + + unsafe + { + result = RtlDeleteFunctionTable(codeCachePtr | 3); + } + + if (!result) + { + throw new InvalidOperationException("Failure removing function table callback."); + } + } + private static unsafe RuntimeFunction* FunctionTableHandler(ulong controlPc, nint context) { int offset = (int)((long)controlPc - context.ToInt64()); diff --git a/src/Ryujinx.Common/Configuration/DirtyHack.cs b/src/Ryujinx.Common/Configuration/DirtyHack.cs index 3959c0a99..71ac88f56 100644 --- a/src/Ryujinx.Common/Configuration/DirtyHack.cs +++ b/src/Ryujinx.Common/Configuration/DirtyHack.cs @@ -9,7 +9,8 @@ namespace Ryujinx.Common.Configuration public enum DirtyHack : byte { Xc2MenuSoftlockFix = 1, - ShaderTranslationDelay = 2 + // ShaderTranslationDelay = 2 + NifmServiceDisableIsAnyInternetRequestAccepted = 3 } public readonly struct EnabledDirtyHack(DirtyHack hack, int value) diff --git a/src/Ryujinx.Common/Configuration/GraphicsBackend.cs b/src/Ryujinx.Common/Configuration/GraphicsBackend.cs index 9d399d560..e3b4f91b0 100644 --- a/src/Ryujinx.Common/Configuration/GraphicsBackend.cs +++ b/src/Ryujinx.Common/Configuration/GraphicsBackend.cs @@ -6,9 +6,7 @@ namespace Ryujinx.Common.Configuration [JsonConverter(typeof(TypedStringEnumConverter))] public enum GraphicsBackend { - Auto, Vulkan, OpenGl, - Metal } } diff --git a/src/Ryujinx.Common/Helpers/RunningPlatform.cs b/src/Ryujinx.Common/Helpers/RunningPlatform.cs index 8d85c4a3c..7ec2f18df 100644 --- a/src/Ryujinx.Common/Helpers/RunningPlatform.cs +++ b/src/Ryujinx.Common/Helpers/RunningPlatform.cs @@ -5,15 +5,34 @@ using System.Runtime.InteropServices; namespace Ryujinx.Common.Helper { + public enum OperatingSystemType + { + MacOS, + Linux, + Windows + } + public static class RunningPlatform { + public static readonly OperatingSystemType CurrentOS + = IsMacOS + ? OperatingSystemType.MacOS + : IsWindows + ? OperatingSystemType.Windows + : IsLinux + ? OperatingSystemType.Linux + : throw new PlatformNotSupportedException(); + + public static Architecture Architecture => RuntimeInformation.OSArchitecture; + public static Architecture CurrentProcessArchitecture => RuntimeInformation.ProcessArchitecture; + public static bool IsMacOS => OperatingSystem.IsMacOS(); public static bool IsWindows => OperatingSystem.IsWindows(); public static bool IsLinux => OperatingSystem.IsLinux(); - public static bool IsArm => RuntimeInformation.OSArchitecture is Architecture.Arm64; + public static bool IsArm => Architecture is Architecture.Arm64; - public static bool IsX64 => RuntimeInformation.OSArchitecture is Architecture.X64; + public static bool IsX64 => Architecture is Architecture.X64; public static bool IsIntelMac => IsMacOS && IsX64; public static bool IsArmMac => IsMacOS && IsArm; diff --git a/src/Ryujinx.Common/ReleaseInformation.cs b/src/Ryujinx.Common/ReleaseInformation.cs index cbf93013f..68db97465 100644 --- a/src/Ryujinx.Common/ReleaseInformation.cs +++ b/src/Ryujinx.Common/ReleaseInformation.cs @@ -37,9 +37,9 @@ namespace Ryujinx.Common public static string GetChangelogUrl(Version currentVersion, Version newVersion) => IsCanaryBuild ? $"https://github.com/{ReleaseChannelOwner}/{ReleaseChannelSourceRepo}/compare/Canary-{currentVersion}...Canary-{newVersion}" - : $"https://github.com/{ReleaseChannelOwner}/{ReleaseChannelSourceRepo}/releases/tag/{newVersion}"; + : GetChangelogForVersion(newVersion); public static string GetChangelogForVersion(Version version) => - $"https://github.com/{ReleaseChannelOwner}/{ReleaseChannelRepo}/releases/tag/{version}"; + $"https://github.com/{ReleaseChannelOwner}/{ReleaseChannelRepo}/releases/{version}"; } } diff --git a/src/Ryujinx.Common/SharedConstants.cs b/src/Ryujinx.Common/SharedConstants.cs new file mode 100644 index 000000000..f40afeb2b --- /dev/null +++ b/src/Ryujinx.Common/SharedConstants.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.Common +{ + public static class SharedConstants + { + public const string DefaultLanPlayHost = "ryuldn.vudjun.com"; + public const short LanPlayPort = 30456; + public const string DefaultLanPlayWebHost = "ryuldnweb.vudjun.com"; + } +} diff --git a/src/Ryujinx.Common/TitleIDs.cs b/src/Ryujinx.Common/TitleIDs.cs index 1bf788c96..76a8a7126 100644 --- a/src/Ryujinx.Common/TitleIDs.cs +++ b/src/Ryujinx.Common/TitleIDs.cs @@ -1,7 +1,4 @@ using Gommon; -using Ryujinx.Common.Configuration; -using Ryujinx.Common.Helper; -using System; using System.Linq; namespace Ryujinx.Common @@ -10,54 +7,6 @@ namespace Ryujinx.Common { public static ReactiveObject> CurrentApplication { get; } = new(); - public static GraphicsBackend SelectGraphicsBackend(string titleId, GraphicsBackend currentBackend) - { - switch (currentBackend) - { - case GraphicsBackend.Metal when !OperatingSystem.IsMacOS(): - case GraphicsBackend.OpenGl when OperatingSystem.IsMacOS(): - return GraphicsBackend.Vulkan; - case GraphicsBackend.Vulkan or GraphicsBackend.OpenGl or GraphicsBackend.Metal: - return currentBackend; - } - - if (!RunningPlatform.IsArmMac) - return GraphicsBackend.Vulkan; - - return GreatMetalTitles.ContainsIgnoreCase(titleId) ? GraphicsBackend.Metal : GraphicsBackend.Vulkan; - } - - public static readonly string[] GreatMetalTitles = - [ - "01009b500007c000", // ARMS - "0100a5c00d162000", // Cuphead - "010023800d64a000", // Deltarune - "01003a30012c0000", // LEGO City Undercover - "010048701995e000", // Luigi's Manion 2 HD - "010028600EBDA000", // Mario 3D World - "0100152000022000", // Mario Kart 8 Deluxe - "010075a016a3a000", // Persona 4 Arena Ultimax - "0100187003A36000", // Pokémon: Let's Go, Eevee! - "010003f003a34000", // Pokémon: Let's Go, Pikachu! - "01008C0016544000", // Sea of Stars - "01006A800016E000", // Smash Ultimate - "01006bb00c6f0000", // The Legend of Zelda: Link's Awakening - - // These ones have small issues, but those happen on Vulkan as well: - "01006f8002326000", // Animal Crossings: New Horizons - "01009bf0072d4000", // Captain Toad: Treasure Tracker - "01009510001ca000", // Fast RMX - "01005CA01580E000", // Persona 5 Royal - "0100b880154fc000", // Persona 5 The Royal (Japan) - "010015100b514000", // Super Mario Bros. Wonder - "0100000000010000", // Super Mario Odyssey - - // Further testing is appreciated, I did not test the entire game: - //"010076f0049a2000", // Bayonetta - //"0100cf5010fec000", // Bayonetta Origins: Cereza and the Lost Demon - //"0100f4300bf2c000", // New Pokemon Snap - ]; - public static string GetDiscordGameAsset(string titleId) => DiscordGameAssetKeys.Contains(titleId) ? titleId : "game"; diff --git a/src/Ryujinx.Graphics.GAL/ComputeSize.cs b/src/Ryujinx.Graphics.GAL/ComputeSize.cs deleted file mode 100644 index c8d89c0fe..000000000 --- a/src/Ryujinx.Graphics.GAL/ComputeSize.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace Ryujinx.Graphics.GAL -{ - public readonly struct ComputeSize - { - public readonly static ComputeSize VtgAsCompute = new(32, 32, 1); - - public readonly int X; - public readonly int Y; - public readonly int Z; - - public ComputeSize(int x, int y, int z) - { - X = x; - Y = y; - Z = z; - } - } -} diff --git a/src/Ryujinx.Graphics.GAL/Format.cs b/src/Ryujinx.Graphics.GAL/Format.cs index b1eb68f72..17c42d2d4 100644 --- a/src/Ryujinx.Graphics.GAL/Format.cs +++ b/src/Ryujinx.Graphics.GAL/Format.cs @@ -339,84 +339,6 @@ namespace Ryujinx.Graphics.GAL return 1; } - /// - /// Get bytes per element for this format. - /// - /// Texture format - /// Byte size for an element of this format (pixel, vertex attribute, etc) - public static int GetBytesPerElement(this Format format) - { - int scalarSize = format.GetScalarSize(); - - switch (format) - { - case Format.R8G8Unorm: - case Format.R8G8Snorm: - case Format.R8G8Uint: - case Format.R8G8Sint: - case Format.R8G8Uscaled: - case Format.R8G8Sscaled: - case Format.R16G16Float: - case Format.R16G16Unorm: - case Format.R16G16Snorm: - case Format.R16G16Uint: - case Format.R16G16Sint: - case Format.R16G16Uscaled: - case Format.R16G16Sscaled: - case Format.R32G32Float: - case Format.R32G32Uint: - case Format.R32G32Sint: - case Format.R32G32Uscaled: - case Format.R32G32Sscaled: - return 2 * scalarSize; - - case Format.R8G8B8Unorm: - case Format.R8G8B8Snorm: - case Format.R8G8B8Uint: - case Format.R8G8B8Sint: - case Format.R8G8B8Uscaled: - case Format.R8G8B8Sscaled: - case Format.R16G16B16Float: - case Format.R16G16B16Unorm: - case Format.R16G16B16Snorm: - case Format.R16G16B16Uint: - case Format.R16G16B16Sint: - case Format.R16G16B16Uscaled: - case Format.R16G16B16Sscaled: - case Format.R32G32B32Float: - case Format.R32G32B32Uint: - case Format.R32G32B32Sint: - case Format.R32G32B32Uscaled: - case Format.R32G32B32Sscaled: - return 3 * scalarSize; - - case Format.R8G8B8A8Unorm: - case Format.R8G8B8A8Snorm: - case Format.R8G8B8A8Uint: - case Format.R8G8B8A8Sint: - case Format.R8G8B8A8Srgb: - case Format.R8G8B8A8Uscaled: - case Format.R8G8B8A8Sscaled: - case Format.B8G8R8A8Unorm: - case Format.B8G8R8A8Srgb: - case Format.R16G16B16A16Float: - case Format.R16G16B16A16Unorm: - case Format.R16G16B16A16Snorm: - case Format.R16G16B16A16Uint: - case Format.R16G16B16A16Sint: - case Format.R16G16B16A16Uscaled: - case Format.R16G16B16A16Sscaled: - case Format.R32G32B32A32Float: - case Format.R32G32B32A32Uint: - case Format.R32G32B32A32Sint: - case Format.R32G32B32A32Uscaled: - case Format.R32G32B32A32Sscaled: - return 4 * scalarSize; - } - - return scalarSize; - } - /// /// Checks if the texture format is a depth or depth-stencil format. /// diff --git a/src/Ryujinx.Graphics.GAL/IRenderer.cs b/src/Ryujinx.Graphics.GAL/IRenderer.cs index c2fdcbe4b..9d822e7c2 100644 --- a/src/Ryujinx.Graphics.GAL/IRenderer.cs +++ b/src/Ryujinx.Graphics.GAL/IRenderer.cs @@ -1,4 +1,6 @@ using Ryujinx.Common.Configuration; +using Ryujinx.Common.Logging; +using Ryujinx.Graphics.GAL.Multithreading; using System; using System.Threading; @@ -10,6 +12,20 @@ namespace Ryujinx.Graphics.GAL bool PreferThreading { get; } + public IRenderer TryMakeThreaded(BackendThreading backendThreading = BackendThreading.Auto) + { + if (backendThreading is BackendThreading.On || + (backendThreading is BackendThreading.Auto && PreferThreading)) + { + Logger.Info?.PrintMsg(LogClass.Gpu, $"Backend Threading ({backendThreading}): True"); + return new ThreadedRenderer(this); + } + + Logger.Info?.PrintMsg(LogClass.Gpu, $"Backend Threading ({backendThreading}): False"); + + return this; + } + IPipeline Pipeline { get; } IWindow Window { get; } diff --git a/src/Ryujinx.Graphics.GAL/ShaderInfo.cs b/src/Ryujinx.Graphics.GAL/ShaderInfo.cs index c7965a03d..2fd3227dc 100644 --- a/src/Ryujinx.Graphics.GAL/ShaderInfo.cs +++ b/src/Ryujinx.Graphics.GAL/ShaderInfo.cs @@ -4,22 +4,23 @@ namespace Ryujinx.Graphics.GAL { public int FragmentOutputMap { get; } public ResourceLayout ResourceLayout { get; } - public ComputeSize ComputeLocalSize { get; } public ProgramPipelineState? State { get; } public bool FromCache { get; set; } - public ShaderInfo( - int fragmentOutputMap, - ResourceLayout resourceLayout, - ComputeSize computeLocalSize, - ProgramPipelineState? state, - bool fromCache = false) + public ShaderInfo(int fragmentOutputMap, ResourceLayout resourceLayout, ProgramPipelineState state, bool fromCache = false) { FragmentOutputMap = fragmentOutputMap; ResourceLayout = resourceLayout; - ComputeLocalSize = computeLocalSize; State = state; FromCache = fromCache; } + + public ShaderInfo(int fragmentOutputMap, ResourceLayout resourceLayout, bool fromCache = false) + { + FragmentOutputMap = fragmentOutputMap; + ResourceLayout = resourceLayout; + State = null; + FromCache = fromCache; + } } } diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index 4ef0caced..22ffe4812 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -324,11 +324,6 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache bool loadHostCache = header.CodeGenVersion == CodeGenVersion; - if (context.Capabilities.Api == TargetApi.Metal) - { - loadHostCache = false; - } - int programIndex = 0; DataEntry entry = new(); @@ -397,8 +392,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache context, shaders, specState.PipelineState, - specState.TransformFeedbackDescriptors != null, - specState.ComputeState.GetLocalSize()); + specState.TransformFeedbackDescriptors != null); IProgram hostProgram; @@ -635,10 +629,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache return; } - if (context.Capabilities.Api != TargetApi.Metal) - { - WriteHostCode(context, hostCode, program.Shaders, streams, timestamp); - } + WriteHostCode(context, hostCode, program.Shaders, streams, timestamp); } /// diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs index f8cb6c56b..a0d3e8c15 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs @@ -1,4 +1,3 @@ -using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Shader; @@ -367,9 +366,6 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache { try { - if (_context.Capabilities.Api == TargetApi.Metal && _context.DirtyHacks.IsEnabled(DirtyHack.ShaderTranslationDelay)) - Thread.Sleep(_context.DirtyHacks[DirtyHack.ShaderTranslationDelay]); - AsyncProgramTranslation asyncTranslation = new(guestShaders, specState, programIndex, isCompute); _asyncTranslationQueue.Add(asyncTranslation, _cancellationToken); } @@ -494,12 +490,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache { ShaderSource[] shaderSources = new ShaderSource[compilation.TranslatedStages.Length]; - ref GpuChannelComputeState computeState = ref compilation.SpecializationState.ComputeState; - - ShaderInfoBuilder shaderInfoBuilder = new( - _context, - compilation.SpecializationState.TransformFeedbackDescriptors != null, - computeLocalSize: computeState.GetLocalSize()); + ShaderInfoBuilder shaderInfoBuilder = new(_context, compilation.SpecializationState.TransformFeedbackDescriptors != null); for (int index = 0; index < compilation.TranslatedStages.Length; index++) { diff --git a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs index 2f1563032..6bb2e5385 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs @@ -18,7 +18,7 @@ namespace Ryujinx.Graphics.Gpu.Shader private readonly GpuAccessorState _state; private readonly int _stageIndex; private readonly bool _compute; - private readonly bool _isOpenGL; + private readonly bool _isVulkan; private readonly bool _hasGeometryShader; private readonly bool _supportsQuads; @@ -40,7 +40,7 @@ namespace Ryujinx.Graphics.Gpu.Shader _channel = channel; _state = state; _stageIndex = stageIndex; - _isOpenGL = context.Capabilities.Api == TargetApi.OpenGL; + _isVulkan = context.Capabilities.Api == TargetApi.Vulkan; _hasGeometryShader = hasGeometryShader; _supportsQuads = context.Capabilities.SupportsQuads; @@ -118,10 +118,10 @@ namespace Ryujinx.Graphics.Gpu.Shader public GpuGraphicsState QueryGraphicsState() { return _state.GraphicsState.CreateShaderGraphicsState( - _isOpenGL, + !_isVulkan, _supportsQuads, _hasGeometryShader, - !_isOpenGL || _state.GraphicsState.YNegateEnabled); + _isVulkan || _state.GraphicsState.YNegateEnabled); } /// diff --git a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs index 701ff764a..d89eebabf 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessorBase.cs @@ -55,7 +55,7 @@ namespace Ryujinx.Graphics.Gpu.Shader { int binding; - if (_context.Capabilities.Api != TargetApi.OpenGL) + if (_context.Capabilities.Api == TargetApi.Vulkan) { binding = GetBindingFromIndex(index, _context.Capabilities.MaximumUniformBuffersPerStage, "Uniform buffer"); } @@ -71,7 +71,7 @@ namespace Ryujinx.Graphics.Gpu.Shader { int binding; - if (_context.Capabilities.Api != TargetApi.OpenGL) + if (_context.Capabilities.Api == TargetApi.Vulkan) { if (count == 1) { @@ -103,7 +103,7 @@ namespace Ryujinx.Graphics.Gpu.Shader { int binding; - if (_context.Capabilities.Api != TargetApi.OpenGL) + if (_context.Capabilities.Api == TargetApi.Vulkan) { binding = GetBindingFromIndex(index, _context.Capabilities.MaximumStorageBuffersPerStage, "Storage buffer"); } @@ -119,7 +119,7 @@ namespace Ryujinx.Graphics.Gpu.Shader { int binding; - if (_context.Capabilities.Api != TargetApi.OpenGL) + if (_context.Capabilities.Api == TargetApi.Vulkan) { if (count == 1) { diff --git a/src/Ryujinx.Graphics.Gpu/Shader/GpuChannelComputeState.cs b/src/Ryujinx.Graphics.Gpu/Shader/GpuChannelComputeState.cs index 720f7e796..d8cdbc348 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/GpuChannelComputeState.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/GpuChannelComputeState.cs @@ -1,5 +1,3 @@ -using Ryujinx.Graphics.GAL; - namespace Ryujinx.Graphics.Gpu.Shader { /// @@ -63,14 +61,5 @@ namespace Ryujinx.Graphics.Gpu.Shader SharedMemorySize = sharedMemorySize; HasUnalignedStorageBuffer = hasUnalignedStorageBuffer; } - - /// - /// Gets the local group size of the shader in a GAL compatible struct. - /// - /// Local group size - public ComputeSize GetLocalSize() - { - return new ComputeSize(LocalSizeX, LocalSizeY, LocalSizeZ); - } } } diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs index 0285d5517..11b566ad5 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs @@ -117,7 +117,7 @@ namespace Ryujinx.Graphics.Gpu.Shader private static string GetDiskCachePath() { return GraphicsConfig.EnableShaderCache && GraphicsConfig.TitleId != null - ? Path.Combine(AppDataManager.GamesDirPath, GraphicsConfig.TitleId.ToLower(), "cache", "shader") + ? Path.Combine(AppDataManager.GamesDirPath, GraphicsConfig.TitleId, "cache", "shader") : null; } @@ -204,7 +204,7 @@ namespace Ryujinx.Graphics.Gpu.Shader GpuChannelComputeState computeState, ulong gpuVa) { - if (_cpPrograms.TryGetValue(gpuVa, out CachedShaderProgram cpShader) && IsShaderEqual(channel, poolState, computeState, cpShader, gpuVa)) + if (_cpPrograms.TryGetValue(gpuVa, out var cpShader) && IsShaderEqual(channel, poolState, computeState, cpShader, gpuVa)) { return cpShader; } @@ -223,11 +223,8 @@ namespace Ryujinx.Graphics.Gpu.Shader TranslatorContext translatorContext = DecodeComputeShader(gpuAccessor, _context.Capabilities.Api, gpuVa); TranslatedShader translatedShader = TranslateShader(_dumper, channel, translatorContext, cachedGuestCode, asCompute: false); - ShaderSource[] shaderSourcesArray = [CreateShaderSource(translatedShader.Program)]; - ShaderInfo info = ShaderInfoBuilder.BuildForCompute( - _context, - translatedShader.Program.Info, - computeState.GetLocalSize()); + ShaderSource[] shaderSourcesArray = new ShaderSource[] { CreateShaderSource(translatedShader.Program) }; + ShaderInfo info = ShaderInfoBuilder.BuildForCompute(_context, translatedShader.Program.Info); IProgram hostProgram = _context.Renderer.CreateProgram(shaderSourcesArray, info); cpShader = new CachedShaderProgram(hostProgram, specState, translatedShader.Shader); @@ -254,8 +251,8 @@ namespace Ryujinx.Graphics.Gpu.Shader { channel.TextureManager.UpdateRenderTargets(); - RtControl rtControl = state.RtControl; - TextureMsaaMode msaaMode = state.RtMsaaMode; + var rtControl = state.RtControl; + var msaaMode = state.RtMsaaMode; pipeline.SamplesCount = msaaMode.SamplesInX() * msaaMode.SamplesInY(); @@ -265,7 +262,7 @@ namespace Ryujinx.Graphics.Gpu.Shader { int rtIndex = rtControl.UnpackPermutationIndex(index); - RtColorState colorState = state.RtColorState[rtIndex]; + var colorState = state.RtColorState[rtIndex]; if (index >= count || colorState.Format == 0 || colorState.WidthOrStride == 0) { @@ -310,12 +307,12 @@ namespace Ryujinx.Graphics.Gpu.Shader ref GpuChannelGraphicsState graphicsState, ShaderAddresses addresses) { - if (_gpPrograms.TryGetValue(addresses, out CachedShaderProgram gpShaders) && IsShaderEqual(channel, ref poolState, ref graphicsState, gpShaders, addresses)) + if (_gpPrograms.TryGetValue(addresses, out var gpShaders) && IsShaderEqual(channel, ref poolState, ref graphicsState, gpShaders, addresses)) { return gpShaders; } - if (_graphicsShaderCache.TryFind(channel, ref poolState, ref graphicsState, addresses, out gpShaders, out CachedGraphicsGuestCode cachedGuestCode)) + if (_graphicsShaderCache.TryFind(channel, ref poolState, ref graphicsState, addresses, out gpShaders, out var cachedGuestCode)) { _gpPrograms[addresses] = gpShaders; return gpShaders; @@ -368,7 +365,7 @@ namespace Ryujinx.Graphics.Gpu.Shader bool geometryToCompute = ShouldConvertGeometryToCompute(_context, geometryHasStore); CachedShaderStage[] shaders = new CachedShaderStage[Constants.ShaderStages + 1]; - List shaderSources = []; + List shaderSources = new(); TranslatorContext previousStage = null; ShaderInfoBuilder infoBuilder = new(_context, transformFeedbackDescriptors != null, vertexToCompute); @@ -428,8 +425,7 @@ namespace Ryujinx.Graphics.Gpu.Shader TranslatorContext lastInVertexPipeline = geometryToCompute ? translatorContexts[4] ?? currentStage : currentStage; - (program, ShaderProgramInfo vacInfo) = lastInVertexPipeline.GenerateVertexPassthroughForCompute(); - infoBuilder.AddStageInfoVac(vacInfo); + program = lastInVertexPipeline.GenerateVertexPassthroughForCompute(); } else { @@ -534,9 +530,9 @@ namespace Ryujinx.Graphics.Gpu.Shader private ShaderAsCompute CreateHostVertexAsComputeProgram(ShaderProgram program, TranslatorContext context, bool tfEnabled) { ShaderSource source = new(program.Code, program.BinaryCode, ShaderStage.Compute, program.Language); - ShaderInfo info = ShaderInfoBuilder.BuildForVertexAsCompute(_context, program.Info, context.GetVertexAsComputeInfo(), tfEnabled); + ShaderInfo info = ShaderInfoBuilder.BuildForVertexAsCompute(_context, program.Info, tfEnabled); - return new(_context.Renderer.CreateProgram([source], info), program.Info, context.GetResourceReservations()); + return new(_context.Renderer.CreateProgram(new[] { source }, info), program.Info, context.GetResourceReservations()); } /// @@ -586,7 +582,7 @@ namespace Ryujinx.Graphics.Gpu.Shader for (int i = 0; i < Constants.TotalTransformFeedbackBuffers; i++) { - TfState tf = state.TfState[i]; + var tf = state.TfState[i]; descs[i] = new TransformFeedbackDescriptor( tf.BufferIndex, @@ -692,7 +688,7 @@ namespace Ryujinx.Graphics.Gpu.Shader /// The generated translator context public static TranslatorContext DecodeComputeShader(IGpuAccessor gpuAccessor, TargetApi api, ulong gpuVa) { - TranslationOptions options = CreateTranslationOptions(api, DefaultFlags | TranslationFlags.Compute); + var options = CreateTranslationOptions(api, DefaultFlags | TranslationFlags.Compute); return Translator.CreateContext(gpuVa, gpuAccessor, options); } @@ -709,7 +705,7 @@ namespace Ryujinx.Graphics.Gpu.Shader /// The generated translator context public static TranslatorContext DecodeGraphicsShader(IGpuAccessor gpuAccessor, TargetApi api, TranslationFlags flags, ulong gpuVa) { - TranslationOptions options = CreateTranslationOptions(api, flags); + var options = CreateTranslationOptions(api, flags); return Translator.CreateContext(gpuVa, gpuAccessor, options); } @@ -773,7 +769,7 @@ namespace Ryujinx.Graphics.Gpu.Shader /// Compiled graphics shader code private static TranslatedShader TranslateShader(ShaderDumper dumper, GpuChannel channel, TranslatorContext context, byte[] code, bool asCompute) { - MemoryManager memoryManager = channel.MemoryManager; + var memoryManager = channel.MemoryManager; (PhysicalMemory physical, ulong cb1DataAddress) = context.Stage == ShaderStage.Compute ? channel.BufferManager.GetComputeUniformBufferAddress(1) @@ -801,7 +797,7 @@ namespace Ryujinx.Graphics.Gpu.Shader { if (address == MemoryManager.PteUnmapped || size == 0 || physicalMemory == null) { - return []; + return Array.Empty(); } return physicalMemory.GetSpan(address, size).ToArray(); @@ -826,20 +822,16 @@ namespace Ryujinx.Graphics.Gpu.Shader /// /// Creates shader translation options with the requested graphics API and flags. - /// The shader language is chosen based on the current configuration and graphics API. + /// The shader language is choosen based on the current configuration and graphics API. /// /// Target graphics API /// Translation flags /// Translation options private static TranslationOptions CreateTranslationOptions(TargetApi api, TranslationFlags flags) { - TargetLanguage lang = api switch - { - TargetApi.OpenGL => TargetLanguage.Glsl, - TargetApi.Vulkan => GraphicsConfig.EnableSpirvCompilationOnVulkan ? TargetLanguage.Spirv : TargetLanguage.Glsl, - TargetApi.Metal => TargetLanguage.Msl, - _ => throw new NotImplementedException() - }; + TargetLanguage lang = GraphicsConfig.EnableSpirvCompilationOnVulkan && api == TargetApi.Vulkan + ? TargetLanguage.Spirv + : TargetLanguage.Glsl; return new TranslationOptions(lang, api, flags); } diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs index 8aa48e0ce..c74c612a5 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs @@ -22,7 +22,6 @@ namespace Ryujinx.Graphics.Gpu.Shader ResourceStages.Geometry; private readonly GpuContext _context; - private readonly ComputeSize _computeLocalSize; private int _fragmentOutputMap; @@ -40,11 +39,9 @@ namespace Ryujinx.Graphics.Gpu.Shader /// GPU context that owns the shaders that will be added to the builder /// Indicates if the graphics shader is used with transform feedback enabled /// Indicates that the vertex shader will be emulated on a compute shader - /// Indicates the local thread size for a compute shader - public ShaderInfoBuilder(GpuContext context, bool tfEnabled, bool vertexAsCompute = false, ComputeSize computeLocalSize = default) + public ShaderInfoBuilder(GpuContext context, bool tfEnabled, bool vertexAsCompute = false) { _context = context; - _computeLocalSize = computeLocalSize; _fragmentOutputMap = -1; @@ -98,7 +95,7 @@ namespace Ryujinx.Graphics.Gpu.Shader private void PopulateDescriptorAndUsages(ResourceStages stages, ResourceType type, int setIndex, int start, int count, bool write = false) { AddDescriptor(stages, type, setIndex, start, count); - // AddUsage(stages, type, setIndex, start, count, write); + AddUsage(stages, type, setIndex, start, count, write); } /// @@ -162,25 +159,6 @@ namespace Ryujinx.Graphics.Gpu.Shader AddUsage(info.Images, stages, isImage: true); } - public void AddStageInfoVac(ShaderProgramInfo info) - { - ResourceStages stages = info.Stage switch - { - ShaderStage.Compute => ResourceStages.Compute, - ShaderStage.Vertex => ResourceStages.Vertex, - ShaderStage.TessellationControl => ResourceStages.TessellationControl, - ShaderStage.TessellationEvaluation => ResourceStages.TessellationEvaluation, - ShaderStage.Geometry => ResourceStages.Geometry, - ShaderStage.Fragment => ResourceStages.Fragment, - _ => ResourceStages.None, - }; - - AddUsage(info.CBuffers, stages, isStorage: false); - AddUsage(info.SBuffers, stages, isStorage: true); - AddUsage(info.Textures, stages, isImage: false); - AddUsage(info.Images, stages, isImage: true); - } - /// /// Adds a resource descriptor to the list of descriptors. /// @@ -383,7 +361,14 @@ namespace Ryujinx.Graphics.Gpu.Shader ResourceLayout resourceLayout = new(descriptors.AsReadOnly(), usages.AsReadOnly()); - return new ShaderInfo(_fragmentOutputMap, resourceLayout, _computeLocalSize, pipeline, fromCache); + if (pipeline.HasValue) + { + return new ShaderInfo(_fragmentOutputMap, resourceLayout, pipeline.Value, fromCache); + } + else + { + return new ShaderInfo(_fragmentOutputMap, resourceLayout, fromCache); + } } /// @@ -393,16 +378,14 @@ namespace Ryujinx.Graphics.Gpu.Shader /// Shaders from the disk cache /// Optional pipeline for background compilation /// Indicates if the graphics shader is used with transform feedback enabled - /// Compute local thread size /// Shader information public static ShaderInfo BuildForCache( GpuContext context, IEnumerable programs, ProgramPipelineState? pipeline, - bool tfEnabled, - ComputeSize computeLocalSize) + bool tfEnabled) { - ShaderInfoBuilder builder = new(context, tfEnabled, computeLocalSize: computeLocalSize); + ShaderInfoBuilder builder = new(context, tfEnabled); foreach (CachedShaderStage program in programs) { @@ -420,12 +403,11 @@ namespace Ryujinx.Graphics.Gpu.Shader /// /// GPU context that owns the shader /// Compute shader information - /// Compute local thread size /// True if the compute shader comes from a disk cache, false otherwise /// Shader information - public static ShaderInfo BuildForCompute(GpuContext context, ShaderProgramInfo info, ComputeSize computeLocalSize, bool fromCache = false) + public static ShaderInfo BuildForCompute(GpuContext context, ShaderProgramInfo info, bool fromCache = false) { - ShaderInfoBuilder builder = new(context, tfEnabled: false, vertexAsCompute: false, computeLocalSize: computeLocalSize); + ShaderInfoBuilder builder = new(context, tfEnabled: false, vertexAsCompute: false); builder.AddStageInfo(info); @@ -440,11 +422,10 @@ namespace Ryujinx.Graphics.Gpu.Shader /// Indicates if the graphics shader is used with transform feedback enabled /// True if the compute shader comes from a disk cache, false otherwise /// Shader information - public static ShaderInfo BuildForVertexAsCompute(GpuContext context, ShaderProgramInfo info, ShaderProgramInfo info2, bool tfEnabled, bool fromCache = false) + public static ShaderInfo BuildForVertexAsCompute(GpuContext context, ShaderProgramInfo info, bool tfEnabled, bool fromCache = false) { - ShaderInfoBuilder builder = new(context, tfEnabled, vertexAsCompute: true, computeLocalSize: ComputeSize.VtgAsCompute); + ShaderInfoBuilder builder = new(context, tfEnabled, vertexAsCompute: true); - builder.AddStageInfoVac(info2); builder.AddStageInfo(info, vertexAsCompute: true); return builder.Build(null, fromCache); diff --git a/src/Ryujinx.Graphics.Metal/Auto.cs b/src/Ryujinx.Graphics.Metal/Auto.cs deleted file mode 100644 index 7e79ecbc3..000000000 --- a/src/Ryujinx.Graphics.Metal/Auto.cs +++ /dev/null @@ -1,146 +0,0 @@ -using System; -using System.Diagnostics; -using System.Runtime.Versioning; -using System.Threading; - -namespace Ryujinx.Graphics.Metal -{ - interface IAuto - { - bool HasCommandBufferDependency(CommandBufferScoped cbs); - - void IncrementReferenceCount(); - void DecrementReferenceCount(int cbIndex); - void DecrementReferenceCount(); - } - - interface IAutoPrivate : IAuto - { - void AddCommandBufferDependencies(CommandBufferScoped cbs); - } - - [SupportedOSPlatform("macos")] - class Auto : IAutoPrivate, IDisposable where T : IDisposable - { - private int _referenceCount; - private T _value; - - private readonly BitMap _cbOwnership; - private readonly MultiFenceHolder _waitable; - - private bool _disposed; - private bool _destroyed; - - public Auto(T value) - { - _referenceCount = 1; - _value = value; - _cbOwnership = new BitMap(CommandBufferPool.MaxCommandBuffers); - } - - public Auto(T value, MultiFenceHolder waitable) : this(value) - { - _waitable = waitable; - } - - public T Get(CommandBufferScoped cbs, int offset, int size, bool write = false) - { - _waitable?.AddBufferUse(cbs.CommandBufferIndex, offset, size, write); - return Get(cbs); - } - - public T GetUnsafe() - { - return _value; - } - - public T Get(CommandBufferScoped cbs) - { - if (!_destroyed) - { - AddCommandBufferDependencies(cbs); - } - - return _value; - } - - public bool HasCommandBufferDependency(CommandBufferScoped cbs) - { - return _cbOwnership.IsSet(cbs.CommandBufferIndex); - } - - public bool HasRentedCommandBufferDependency(CommandBufferPool cbp) - { - return _cbOwnership.AnySet(); - } - - public void AddCommandBufferDependencies(CommandBufferScoped cbs) - { - // We don't want to add a reference to this object to the command buffer - // more than once, so if we detect that the command buffer already has ownership - // of this object, then we can just return without doing anything else. - if (_cbOwnership.Set(cbs.CommandBufferIndex)) - { - if (_waitable != null) - { - cbs.AddWaitable(_waitable); - } - - cbs.AddDependant(this); - } - } - - public bool TryIncrementReferenceCount() - { - int lastValue; - do - { - lastValue = _referenceCount; - - if (lastValue == 0) - { - return false; - } - } - while (Interlocked.CompareExchange(ref _referenceCount, lastValue + 1, lastValue) != lastValue); - - return true; - } - - public void IncrementReferenceCount() - { - if (Interlocked.Increment(ref _referenceCount) == 1) - { - Interlocked.Decrement(ref _referenceCount); - throw new InvalidOperationException("Attempted to increment the reference count of an object that was already destroyed."); - } - } - - public void DecrementReferenceCount(int cbIndex) - { - _cbOwnership.Clear(cbIndex); - DecrementReferenceCount(); - } - - public void DecrementReferenceCount() - { - if (Interlocked.Decrement(ref _referenceCount) == 0) - { - _value.Dispose(); - _value = default; - _destroyed = true; - } - - Debug.Assert(_referenceCount >= 0); - } - - public void Dispose() - { - if (!_disposed) - { - DecrementReferenceCount(); - _disposed = true; - } - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/BackgroundResources.cs b/src/Ryujinx.Graphics.Metal/BackgroundResources.cs deleted file mode 100644 index e38b877a6..000000000 --- a/src/Ryujinx.Graphics.Metal/BackgroundResources.cs +++ /dev/null @@ -1,107 +0,0 @@ -using SharpMetal.Metal; -using System; -using System.Collections.Generic; -using System.Runtime.Versioning; -using System.Threading; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - class BackgroundResource : IDisposable - { - private readonly MetalRenderer _renderer; - - private CommandBufferPool _pool; - private PersistentFlushBuffer _flushBuffer; - - public BackgroundResource(MetalRenderer renderer) - { - _renderer = renderer; - } - - public CommandBufferPool GetPool() - { - if (_pool == null) - { - MTLCommandQueue queue = _renderer.BackgroundQueue; - _pool = new CommandBufferPool(queue, true); - _pool.Initialize(null); // TODO: Proper encoder factory for background render/compute - } - - return _pool; - } - - public PersistentFlushBuffer GetFlushBuffer() - { - _flushBuffer ??= new PersistentFlushBuffer(_renderer); - - return _flushBuffer; - } - - public void Dispose() - { - _pool?.Dispose(); - _flushBuffer?.Dispose(); - } - } - - [SupportedOSPlatform("macos")] - class BackgroundResources : IDisposable - { - private readonly MetalRenderer _renderer; - - private readonly Dictionary _resources; - - public BackgroundResources(MetalRenderer renderer) - { - _renderer = renderer; - - _resources = new Dictionary(); - } - - private void Cleanup() - { - lock (_resources) - { - foreach (KeyValuePair tuple in _resources) - { - if (!tuple.Key.IsAlive) - { - tuple.Value.Dispose(); - _resources.Remove(tuple.Key); - } - } - } - } - - public BackgroundResource Get() - { - Thread thread = Thread.CurrentThread; - - lock (_resources) - { - if (!_resources.TryGetValue(thread, out BackgroundResource resource)) - { - Cleanup(); - - resource = new BackgroundResource(_renderer); - - _resources[thread] = resource; - } - - return resource; - } - } - - public void Dispose() - { - lock (_resources) - { - foreach (BackgroundResource resource in _resources.Values) - { - resource.Dispose(); - } - } - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/BitMap.cs b/src/Ryujinx.Graphics.Metal/BitMap.cs deleted file mode 100644 index 4ddc438c1..000000000 --- a/src/Ryujinx.Graphics.Metal/BitMap.cs +++ /dev/null @@ -1,157 +0,0 @@ -namespace Ryujinx.Graphics.Metal -{ - readonly struct BitMap - { - public const int IntSize = 64; - - private const int IntShift = 6; - private const int IntMask = IntSize - 1; - - private readonly long[] _masks; - - public BitMap(int count) - { - _masks = new long[(count + IntMask) / IntSize]; - } - - public bool AnySet() - { - for (int i = 0; i < _masks.Length; i++) - { - if (_masks[i] != 0) - { - return true; - } - } - - return false; - } - - public bool IsSet(int bit) - { - int wordIndex = bit >> IntShift; - int wordBit = bit & IntMask; - - long wordMask = 1L << wordBit; - - return (_masks[wordIndex] & wordMask) != 0; - } - - public bool IsSet(int start, int end) - { - if (start == end) - { - return IsSet(start); - } - - int startIndex = start >> IntShift; - int startBit = start & IntMask; - long startMask = -1L << startBit; - - int endIndex = end >> IntShift; - int endBit = end & IntMask; - long endMask = (long)(ulong.MaxValue >> (IntMask - endBit)); - - if (startIndex == endIndex) - { - return (_masks[startIndex] & startMask & endMask) != 0; - } - - if ((_masks[startIndex] & startMask) != 0) - { - return true; - } - - for (int i = startIndex + 1; i < endIndex; i++) - { - if (_masks[i] != 0) - { - return true; - } - } - - if ((_masks[endIndex] & endMask) != 0) - { - return true; - } - - return false; - } - - public bool Set(int bit) - { - int wordIndex = bit >> IntShift; - int wordBit = bit & IntMask; - - long wordMask = 1L << wordBit; - - if ((_masks[wordIndex] & wordMask) != 0) - { - return false; - } - - _masks[wordIndex] |= wordMask; - - return true; - } - - public void SetRange(int start, int end) - { - if (start == end) - { - Set(start); - return; - } - - int startIndex = start >> IntShift; - int startBit = start & IntMask; - long startMask = -1L << startBit; - - int endIndex = end >> IntShift; - int endBit = end & IntMask; - long endMask = (long)(ulong.MaxValue >> (IntMask - endBit)); - - if (startIndex == endIndex) - { - _masks[startIndex] |= startMask & endMask; - } - else - { - _masks[startIndex] |= startMask; - - for (int i = startIndex + 1; i < endIndex; i++) - { - _masks[i] |= -1; - } - - _masks[endIndex] |= endMask; - } - } - - public void Clear(int bit) - { - int wordIndex = bit >> IntShift; - int wordBit = bit & IntMask; - - long wordMask = 1L << wordBit; - - _masks[wordIndex] &= ~wordMask; - } - - public void Clear() - { - for (int i = 0; i < _masks.Length; i++) - { - _masks[i] = 0; - } - } - - public void ClearInt(int start, int end) - { - for (int i = start; i <= end; i++) - { - _masks[i] = 0; - } - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/BufferHolder.cs b/src/Ryujinx.Graphics.Metal/BufferHolder.cs deleted file mode 100644 index 630571658..000000000 --- a/src/Ryujinx.Graphics.Metal/BufferHolder.cs +++ /dev/null @@ -1,385 +0,0 @@ -using Ryujinx.Graphics.GAL; -using SharpMetal.Metal; -using System; -using System.Runtime.InteropServices; -using System.Runtime.Versioning; -using System.Threading; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - class BufferHolder : IDisposable - { - private CacheByRange _cachedConvertedBuffers; - - public int Size { get; } - - private readonly IntPtr _map; - private readonly MetalRenderer _renderer; - private readonly Pipeline _pipeline; - - private readonly MultiFenceHolder _waitable; - private readonly Auto _buffer; - - private readonly ReaderWriterLockSlim _flushLock; - private FenceHolder _flushFence; - private int _flushWaiting; - - public BufferHolder(MetalRenderer renderer, Pipeline pipeline, MTLBuffer buffer, int size) - { - _renderer = renderer; - _pipeline = pipeline; - _map = buffer.Contents; - _waitable = new MultiFenceHolder(size); - _buffer = new Auto(new(buffer), _waitable); - - _flushLock = new ReaderWriterLockSlim(); - - Size = size; - } - - public Auto GetBuffer() - { - return _buffer; - } - - public Auto GetBuffer(bool isWrite) - { - if (isWrite) - { - SignalWrite(0, Size); - } - - return _buffer; - } - - public Auto GetBuffer(int offset, int size, bool isWrite) - { - if (isWrite) - { - SignalWrite(offset, size); - } - - return _buffer; - } - - public void SignalWrite(int offset, int size) - { - if (offset == 0 && size == Size) - { - _cachedConvertedBuffers.Clear(); - } - else - { - _cachedConvertedBuffers.ClearRange(offset, size); - } - } - - private void ClearFlushFence() - { - // Assumes _flushLock is held as writer. - - if (_flushFence != null) - { - if (_flushWaiting == 0) - { - _flushFence.Put(); - } - - _flushFence = null; - } - } - - private void WaitForFlushFence() - { - if (_flushFence == null) - { - return; - } - - // If storage has changed, make sure the fence has been reached so that the data is in place. - _flushLock.ExitReadLock(); - _flushLock.EnterWriteLock(); - - if (_flushFence != null) - { - FenceHolder fence = _flushFence; - Interlocked.Increment(ref _flushWaiting); - - // Don't wait in the lock. - - _flushLock.ExitWriteLock(); - - fence.Wait(); - - _flushLock.EnterWriteLock(); - - if (Interlocked.Decrement(ref _flushWaiting) == 0) - { - fence.Put(); - } - - _flushFence = null; - } - - // Assumes the _flushLock is held as reader, returns in same state. - _flushLock.ExitWriteLock(); - _flushLock.EnterReadLock(); - } - - public PinnedSpan GetData(int offset, int size) - { - _flushLock.EnterReadLock(); - - WaitForFlushFence(); - - Span result; - - if (_map != IntPtr.Zero) - { - result = GetDataStorage(offset, size); - - // Need to be careful here, the buffer can't be unmapped while the data is being used. - _buffer.IncrementReferenceCount(); - - _flushLock.ExitReadLock(); - - return PinnedSpan.UnsafeFromSpan(result, _buffer.DecrementReferenceCount); - } - - throw new InvalidOperationException("The buffer is not mapped"); - } - - public unsafe Span GetDataStorage(int offset, int size) - { - int mappingSize = Math.Min(size, Size - offset); - - if (_map != IntPtr.Zero) - { - return new Span((void*)(_map + offset), mappingSize); - } - - throw new InvalidOperationException("The buffer is not mapped."); - } - - public unsafe void SetData(int offset, ReadOnlySpan data, CommandBufferScoped? cbs = null, bool allowCbsWait = true) - { - int dataSize = Math.Min(data.Length, Size - offset); - if (dataSize == 0) - { - return; - } - - if (_map != IntPtr.Zero) - { - // If persistently mapped, set the data directly if the buffer is not currently in use. - bool isRented = _buffer.HasRentedCommandBufferDependency(_renderer.CommandBufferPool); - - // If the buffer is rented, take a little more time and check if the use overlaps this handle. - bool needsFlush = isRented && _waitable.IsBufferRangeInUse(offset, dataSize, false); - - if (!needsFlush) - { - WaitForFences(offset, dataSize); - - data[..dataSize].CopyTo(new Span((void*)(_map + offset), dataSize)); - - SignalWrite(offset, dataSize); - - return; - } - } - - if (cbs != null && - cbs.Value.Encoders.CurrentEncoderType == EncoderType.Render && - !(_buffer.HasCommandBufferDependency(cbs.Value) && - _waitable.IsBufferRangeInUse(cbs.Value.CommandBufferIndex, offset, dataSize))) - { - // If the buffer hasn't been used on the command buffer yet, try to preload the data. - // This avoids ending and beginning render passes on each buffer data upload. - - cbs = _pipeline.GetPreloadCommandBuffer(); - } - - if (allowCbsWait) - { - _renderer.BufferManager.StagingBuffer.PushData(_renderer.CommandBufferPool, cbs, this, offset, data); - } - else - { - bool rentCbs = cbs == null; - if (rentCbs) - { - cbs = _renderer.CommandBufferPool.Rent(); - } - - if (!_renderer.BufferManager.StagingBuffer.TryPushData(cbs.Value, this, offset, data)) - { - // Need to do a slow upload. - BufferHolder srcHolder = _renderer.BufferManager.Create(dataSize); - srcHolder.SetDataUnchecked(0, data); - - Auto srcBuffer = srcHolder.GetBuffer(); - Auto dstBuffer = this.GetBuffer(true); - - Copy(cbs.Value, srcBuffer, dstBuffer, 0, offset, dataSize); - - srcHolder.Dispose(); - } - - if (rentCbs) - { - cbs.Value.Dispose(); - } - } - } - - public unsafe void SetDataUnchecked(int offset, ReadOnlySpan data) - { - int dataSize = Math.Min(data.Length, Size - offset); - if (dataSize == 0) - { - return; - } - - if (_map != IntPtr.Zero) - { - data[..dataSize].CopyTo(new Span((void*)(_map + offset), dataSize)); - } - } - - public void SetDataUnchecked(int offset, ReadOnlySpan data) where T : unmanaged - { - SetDataUnchecked(offset, MemoryMarshal.AsBytes(data)); - } - - public static void Copy( - CommandBufferScoped cbs, - Auto src, - Auto dst, - int srcOffset, - int dstOffset, - int size, - bool registerSrcUsage = true) - { - MTLBuffer srcBuffer = registerSrcUsage ? src.Get(cbs, srcOffset, size).Value : src.GetUnsafe().Value; - MTLBuffer dstbuffer = dst.Get(cbs, dstOffset, size, true).Value; - - cbs.Encoders.EnsureBlitEncoder().CopyFromBuffer( - srcBuffer, - (ulong)srcOffset, - dstbuffer, - (ulong)dstOffset, - (ulong)size); - } - - public void WaitForFences() - { - _waitable.WaitForFences(); - } - - public void WaitForFences(int offset, int size) - { - _waitable.WaitForFences(offset, size); - } - - private bool BoundToRange(int offset, ref int size) - { - if (offset >= Size) - { - return false; - } - - size = Math.Min(Size - offset, size); - - return true; - } - - public Auto GetBufferI8ToI16(CommandBufferScoped cbs, int offset, int size) - { - if (!BoundToRange(offset, ref size)) - { - return null; - } - - I8ToI16CacheKey key = new(_renderer); - - if (!_cachedConvertedBuffers.TryGetValue(offset, size, key, out BufferHolder holder)) - { - holder = _renderer.BufferManager.Create((size * 2 + 3) & ~3); - - _renderer.HelperShader.ConvertI8ToI16(cbs, this, holder, offset, size); - - key.SetBuffer(holder.GetBuffer()); - - _cachedConvertedBuffers.Add(offset, size, key, holder); - } - - return holder.GetBuffer(); - } - - public Auto GetBufferTopologyConversion(CommandBufferScoped cbs, int offset, int size, IndexBufferPattern pattern, int indexSize) - { - if (!BoundToRange(offset, ref size)) - { - return null; - } - - TopologyConversionCacheKey key = new(_renderer, pattern, indexSize); - - if (!_cachedConvertedBuffers.TryGetValue(offset, size, key, out BufferHolder holder)) - { - // The destination index size is always I32. - - int indexCount = size / indexSize; - - int convertedCount = pattern.GetConvertedCount(indexCount); - - holder = _renderer.BufferManager.Create(convertedCount * 4); - - _renderer.HelperShader.ConvertIndexBuffer(cbs, this, holder, pattern, indexSize, offset, indexCount); - - key.SetBuffer(holder.GetBuffer()); - - _cachedConvertedBuffers.Add(offset, size, key, holder); - } - - return holder.GetBuffer(); - } - - public bool TryGetCachedConvertedBuffer(int offset, int size, ICacheKey key, out BufferHolder holder) - { - return _cachedConvertedBuffers.TryGetValue(offset, size, key, out holder); - } - - public void AddCachedConvertedBuffer(int offset, int size, ICacheKey key, BufferHolder holder) - { - _cachedConvertedBuffers.Add(offset, size, key, holder); - } - - public void AddCachedConvertedBufferDependency(int offset, int size, ICacheKey key, Dependency dependency) - { - _cachedConvertedBuffers.AddDependency(offset, size, key, dependency); - } - - public void RemoveCachedConvertedBuffer(int offset, int size, ICacheKey key) - { - _cachedConvertedBuffers.Remove(offset, size, key); - } - - - public void Dispose() - { - _pipeline.FlushCommandsIfWeightExceeding(_buffer, (ulong)Size); - - _buffer.Dispose(); - _cachedConvertedBuffers.Dispose(); - - _flushLock.EnterWriteLock(); - - ClearFlushFence(); - - _flushLock.ExitWriteLock(); - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/BufferManager.cs b/src/Ryujinx.Graphics.Metal/BufferManager.cs deleted file mode 100644 index 73a8d6fe7..000000000 --- a/src/Ryujinx.Graphics.Metal/BufferManager.cs +++ /dev/null @@ -1,237 +0,0 @@ -using Ryujinx.Common.Logging; -using Ryujinx.Graphics.GAL; -using SharpMetal.Metal; -using System; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - readonly struct ScopedTemporaryBuffer : IDisposable - { - private readonly BufferManager _bufferManager; - private readonly bool _isReserved; - - public readonly BufferRange Range; - public readonly BufferHolder Holder; - - public BufferHandle Handle => Range.Handle; - public int Offset => Range.Offset; - - public ScopedTemporaryBuffer(BufferManager bufferManager, BufferHolder holder, BufferHandle handle, int offset, int size, bool isReserved) - { - _bufferManager = bufferManager; - - Range = new BufferRange(handle, offset, size); - Holder = holder; - - _isReserved = isReserved; - } - - public void Dispose() - { - if (!_isReserved) - { - _bufferManager.Delete(Range.Handle); - } - } - } - - [SupportedOSPlatform("macos")] - class BufferManager : IDisposable - { - private readonly IdList _buffers; - - private readonly MTLDevice _device; - private readonly MetalRenderer _renderer; - private readonly Pipeline _pipeline; - - public int BufferCount { get; private set; } - - public StagingBuffer StagingBuffer { get; } - - public BufferManager(MTLDevice device, MetalRenderer renderer, Pipeline pipeline) - { - _device = device; - _renderer = renderer; - _pipeline = pipeline; - _buffers = new IdList(); - - StagingBuffer = new StagingBuffer(_renderer, this); - } - - public BufferHandle Create(nint pointer, int size) - { - // TODO: This is the wrong Metal method, we need no-copy which SharpMetal isn't giving us. - MTLBuffer buffer = _device.NewBuffer(pointer, (ulong)size, MTLResourceOptions.ResourceStorageModeShared); - - if (buffer == IntPtr.Zero) - { - Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create buffer with size 0x{size:X}, and pointer 0x{pointer:X}."); - - return BufferHandle.Null; - } - - BufferHolder holder = new(_renderer, _pipeline, buffer, size); - - BufferCount++; - - ulong handle64 = (uint)_buffers.Add(holder); - - return Unsafe.As(ref handle64); - } - - public BufferHandle CreateWithHandle(int size) - { - return CreateWithHandle(size, out _); - } - - public BufferHandle CreateWithHandle(int size, out BufferHolder holder) - { - holder = Create(size); - - if (holder == null) - { - return BufferHandle.Null; - } - - BufferCount++; - - ulong handle64 = (uint)_buffers.Add(holder); - - return Unsafe.As(ref handle64); - } - - public ScopedTemporaryBuffer ReserveOrCreate(CommandBufferScoped cbs, int size) - { - StagingBufferReserved? result = StagingBuffer.TryReserveData(cbs, size); - - if (result.HasValue) - { - return new ScopedTemporaryBuffer(this, result.Value.Buffer, StagingBuffer.Handle, result.Value.Offset, result.Value.Size, true); - } - else - { - // Create a temporary buffer. - BufferHandle handle = CreateWithHandle(size, out BufferHolder holder); - - return new ScopedTemporaryBuffer(this, holder, handle, 0, size, false); - } - } - - public BufferHolder Create(int size) - { - MTLBuffer buffer = _device.NewBuffer((ulong)size, MTLResourceOptions.ResourceStorageModeShared); - - if (buffer != IntPtr.Zero) - { - return new BufferHolder(_renderer, _pipeline, buffer, size); - } - - Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create buffer with size 0x{size:X}."); - - return null; - } - - public Auto GetBuffer(BufferHandle handle, bool isWrite, out int size) - { - if (TryGetBuffer(handle, out BufferHolder holder)) - { - size = holder.Size; - return holder.GetBuffer(isWrite); - } - - size = 0; - return null; - } - - public Auto GetBuffer(BufferHandle handle, int offset, int size, bool isWrite) - { - if (TryGetBuffer(handle, out BufferHolder holder)) - { - return holder.GetBuffer(offset, size, isWrite); - } - - return null; - } - - public Auto GetBuffer(BufferHandle handle, bool isWrite) - { - if (TryGetBuffer(handle, out BufferHolder holder)) - { - return holder.GetBuffer(isWrite); - } - - return null; - } - - public Auto GetBufferI8ToI16(CommandBufferScoped cbs, BufferHandle handle, int offset, int size) - { - if (TryGetBuffer(handle, out BufferHolder holder)) - { - return holder.GetBufferI8ToI16(cbs, offset, size); - } - - return null; - } - - public Auto GetBufferTopologyConversion(CommandBufferScoped cbs, BufferHandle handle, int offset, int size, IndexBufferPattern pattern, int indexSize) - { - if (TryGetBuffer(handle, out BufferHolder holder)) - { - return holder.GetBufferTopologyConversion(cbs, offset, size, pattern, indexSize); - } - - return null; - } - - public PinnedSpan GetData(BufferHandle handle, int offset, int size) - { - if (TryGetBuffer(handle, out BufferHolder holder)) - { - return holder.GetData(offset, size); - } - - return new PinnedSpan(); - } - - public void SetData(BufferHandle handle, int offset, ReadOnlySpan data) where T : unmanaged - { - SetData(handle, offset, MemoryMarshal.Cast(data), null); - } - - public void SetData(BufferHandle handle, int offset, ReadOnlySpan data, CommandBufferScoped? cbs) - { - if (TryGetBuffer(handle, out BufferHolder holder)) - { - holder.SetData(offset, data, cbs); - } - } - - public void Delete(BufferHandle handle) - { - if (TryGetBuffer(handle, out BufferHolder holder)) - { - holder.Dispose(); - _buffers.Remove((int)Unsafe.As(ref handle)); - } - } - - private bool TryGetBuffer(BufferHandle handle, out BufferHolder holder) - { - return _buffers.TryGetValue((int)Unsafe.As(ref handle), out holder); - } - - public void Dispose() - { - StagingBuffer.Dispose(); - - foreach (BufferHolder buffer in _buffers) - { - buffer.Dispose(); - } - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/BufferUsageBitmap.cs b/src/Ryujinx.Graphics.Metal/BufferUsageBitmap.cs deleted file mode 100644 index 379e27407..000000000 --- a/src/Ryujinx.Graphics.Metal/BufferUsageBitmap.cs +++ /dev/null @@ -1,85 +0,0 @@ -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - internal class BufferUsageBitmap - { - private readonly BitMap _bitmap; - private readonly int _size; - private readonly int _granularity; - private readonly int _bits; - private readonly int _writeBitOffset; - - private readonly int _intsPerCb; - private readonly int _bitsPerCb; - - public BufferUsageBitmap(int size, int granularity) - { - _size = size; - _granularity = granularity; - - // There are two sets of bits - one for read tracking, and the other for write. - int bits = (size + (granularity - 1)) / granularity; - _writeBitOffset = bits; - _bits = bits << 1; - - _intsPerCb = (_bits + (BitMap.IntSize - 1)) / BitMap.IntSize; - _bitsPerCb = _intsPerCb * BitMap.IntSize; - - _bitmap = new BitMap(_bitsPerCb * CommandBufferPool.MaxCommandBuffers); - } - - public void Add(int cbIndex, int offset, int size, bool write) - { - if (size == 0) - { - return; - } - - // Some usages can be out of bounds (vertex buffer on amd), so bound if necessary. - if (offset + size > _size) - { - size = _size - offset; - } - - int cbBase = cbIndex * _bitsPerCb + (write ? _writeBitOffset : 0); - int start = cbBase + offset / _granularity; - int end = cbBase + (offset + size - 1) / _granularity; - - _bitmap.SetRange(start, end); - } - - public bool OverlapsWith(int cbIndex, int offset, int size, bool write = false) - { - if (size == 0) - { - return false; - } - - int cbBase = cbIndex * _bitsPerCb + (write ? _writeBitOffset : 0); - int start = cbBase + offset / _granularity; - int end = cbBase + (offset + size - 1) / _granularity; - - return _bitmap.IsSet(start, end); - } - - public bool OverlapsWith(int offset, int size, bool write) - { - for (int i = 0; i < CommandBufferPool.MaxCommandBuffers; i++) - { - if (OverlapsWith(i, offset, size, write)) - { - return true; - } - } - - return false; - } - - public void Clear(int cbIndex) - { - _bitmap.ClearInt(cbIndex * _intsPerCb, (cbIndex + 1) * _intsPerCb - 1); - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/CacheByRange.cs b/src/Ryujinx.Graphics.Metal/CacheByRange.cs deleted file mode 100644 index 2002eeba4..000000000 --- a/src/Ryujinx.Graphics.Metal/CacheByRange.cs +++ /dev/null @@ -1,294 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - interface ICacheKey : IDisposable - { - bool KeyEqual(ICacheKey other); - } - - [SupportedOSPlatform("macos")] - struct I8ToI16CacheKey : ICacheKey - { - // Used to notify the pipeline that bindings have invalidated on dispose. - // private readonly MetalRenderer _renderer; - // private Auto _buffer; - - public I8ToI16CacheKey(MetalRenderer renderer) - { - // _renderer = renderer; - // _buffer = null; - } - - public readonly bool KeyEqual(ICacheKey other) - { - return other is I8ToI16CacheKey; - } - - public readonly void SetBuffer(Auto buffer) - { - // _buffer = buffer; - } - - public readonly void Dispose() - { - // TODO: Tell pipeline buffer is dirty! - // _renderer.PipelineInternal.DirtyIndexBuffer(_buffer); - } - } - - [SupportedOSPlatform("macos")] - readonly struct TopologyConversionCacheKey : ICacheKey - { - private readonly IndexBufferPattern _pattern; - private readonly int _indexSize; - - // Used to notify the pipeline that bindings have invalidated on dispose. - // private readonly MetalRenderer _renderer; - // private Auto _buffer; - - public TopologyConversionCacheKey(MetalRenderer renderer, IndexBufferPattern pattern, int indexSize) - { - // _renderer = renderer; - // _buffer = null; - _pattern = pattern; - _indexSize = indexSize; - } - - public readonly bool KeyEqual(ICacheKey other) - { - return other is TopologyConversionCacheKey entry && - entry._pattern == _pattern && - entry._indexSize == _indexSize; - } - - public void SetBuffer(Auto buffer) - { - // _buffer = buffer; - } - - public readonly void Dispose() - { - // TODO: Tell pipeline buffer is dirty! - // _renderer.PipelineInternal.DirtyVertexBuffer(_buffer); - } - } - - [SupportedOSPlatform("macos")] - readonly struct Dependency - { - private readonly BufferHolder _buffer; - private readonly int _offset; - private readonly int _size; - private readonly ICacheKey _key; - - public Dependency(BufferHolder buffer, int offset, int size, ICacheKey key) - { - _buffer = buffer; - _offset = offset; - _size = size; - _key = key; - } - - public void RemoveFromOwner() - { - _buffer.RemoveCachedConvertedBuffer(_offset, _size, _key); - } - } - - [SupportedOSPlatform("macos")] - struct CacheByRange where T : IDisposable - { - private struct Entry - { - public readonly ICacheKey Key; - public readonly T Value; - public List DependencyList; - - public Entry(ICacheKey key, T value) - { - Key = key; - Value = value; - DependencyList = null; - } - - public readonly void InvalidateDependencies() - { - if (DependencyList != null) - { - foreach (Dependency dependency in DependencyList) - { - dependency.RemoveFromOwner(); - } - - DependencyList.Clear(); - } - } - } - - private Dictionary> _ranges; - - public void Add(int offset, int size, ICacheKey key, T value) - { - List entries = GetEntries(offset, size); - - entries.Add(new Entry(key, value)); - } - - public void AddDependency(int offset, int size, ICacheKey key, Dependency dependency) - { - List entries = GetEntries(offset, size); - - for (int i = 0; i < entries.Count; i++) - { - Entry entry = entries[i]; - - if (entry.Key.KeyEqual(key)) - { - if (entry.DependencyList == null) - { - entry.DependencyList = []; - entries[i] = entry; - } - - entry.DependencyList.Add(dependency); - - break; - } - } - } - - public void Remove(int offset, int size, ICacheKey key) - { - List entries = GetEntries(offset, size); - - for (int i = 0; i < entries.Count; i++) - { - Entry entry = entries[i]; - - if (entry.Key.KeyEqual(key)) - { - entries.RemoveAt(i--); - - DestroyEntry(entry); - } - } - - if (entries.Count == 0) - { - _ranges.Remove(PackRange(offset, size)); - } - } - - public bool TryGetValue(int offset, int size, ICacheKey key, out T value) - { - List entries = GetEntries(offset, size); - - foreach (Entry entry in entries) - { - if (entry.Key.KeyEqual(key)) - { - value = entry.Value; - - return true; - } - } - - value = default; - return false; - } - - public void Clear() - { - if (_ranges != null) - { - foreach (List entries in _ranges.Values) - { - foreach (Entry entry in entries) - { - DestroyEntry(entry); - } - } - - _ranges.Clear(); - _ranges = null; - } - } - - public readonly void ClearRange(int offset, int size) - { - if (_ranges != null && _ranges.Count > 0) - { - int end = offset + size; - - List toRemove = null; - - foreach (KeyValuePair> range in _ranges) - { - (int rOffset, int rSize) = UnpackRange(range.Key); - - int rEnd = rOffset + rSize; - - if (rEnd > offset && rOffset < end) - { - List entries = range.Value; - - foreach (Entry entry in entries) - { - DestroyEntry(entry); - } - - (toRemove ??= []).Add(range.Key); - } - } - - if (toRemove != null) - { - foreach (ulong range in toRemove) - { - _ranges.Remove(range); - } - } - } - } - - private List GetEntries(int offset, int size) - { - _ranges ??= new Dictionary>(); - - ulong key = PackRange(offset, size); - - if (!_ranges.TryGetValue(key, out List value)) - { - value = []; - _ranges.Add(key, value); - } - - return value; - } - - private static void DestroyEntry(Entry entry) - { - entry.Key.Dispose(); - entry.Value?.Dispose(); - entry.InvalidateDependencies(); - } - - private static ulong PackRange(int offset, int size) - { - return (uint)offset | ((ulong)size << 32); - } - - private static (int offset, int size) UnpackRange(ulong range) - { - return ((int)range, (int)(range >> 32)); - } - - public void Dispose() - { - Clear(); - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/CommandBufferEncoder.cs b/src/Ryujinx.Graphics.Metal/CommandBufferEncoder.cs deleted file mode 100644 index 3bc30e239..000000000 --- a/src/Ryujinx.Graphics.Metal/CommandBufferEncoder.cs +++ /dev/null @@ -1,170 +0,0 @@ -using Ryujinx.Graphics.Metal; -using SharpMetal.Metal; -using System; -using System.Runtime.CompilerServices; -using System.Runtime.Versioning; - -interface IEncoderFactory -{ - MTLRenderCommandEncoder CreateRenderCommandEncoder(); - MTLComputeCommandEncoder CreateComputeCommandEncoder(); -} - -/// -/// Tracks active encoder object for a command buffer. -/// -[SupportedOSPlatform("macos")] -class CommandBufferEncoder -{ - public EncoderType CurrentEncoderType { get; private set; } = EncoderType.None; - - public MTLBlitCommandEncoder BlitEncoder => new(CurrentEncoder.Value); - - public MTLComputeCommandEncoder ComputeEncoder => new(CurrentEncoder.Value); - - public MTLRenderCommandEncoder RenderEncoder => new(CurrentEncoder.Value); - - internal MTLCommandEncoder? CurrentEncoder { get; private set; } - - private MTLCommandBuffer _commandBuffer; - private IEncoderFactory _encoderFactory; - - public void Initialize(MTLCommandBuffer commandBuffer, IEncoderFactory encoderFactory) - { - _commandBuffer = commandBuffer; - _encoderFactory = encoderFactory; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public MTLRenderCommandEncoder EnsureRenderEncoder() - { - if (CurrentEncoderType != EncoderType.Render) - { - return BeginRenderPass(); - } - - return RenderEncoder; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public MTLBlitCommandEncoder EnsureBlitEncoder() - { - if (CurrentEncoderType != EncoderType.Blit) - { - return BeginBlitPass(); - } - - return BlitEncoder; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public MTLComputeCommandEncoder EnsureComputeEncoder() - { - if (CurrentEncoderType != EncoderType.Compute) - { - return BeginComputePass(); - } - - return ComputeEncoder; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool TryGetRenderEncoder(out MTLRenderCommandEncoder encoder) - { - if (CurrentEncoderType != EncoderType.Render) - { - encoder = default; - return false; - } - - encoder = RenderEncoder; - return true; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool TryGetBlitEncoder(out MTLBlitCommandEncoder encoder) - { - if (CurrentEncoderType != EncoderType.Blit) - { - encoder = default; - return false; - } - - encoder = BlitEncoder; - return true; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool TryGetComputeEncoder(out MTLComputeCommandEncoder encoder) - { - if (CurrentEncoderType != EncoderType.Compute) - { - encoder = default; - return false; - } - - encoder = ComputeEncoder; - return true; - } - - public void EndCurrentPass() - { - if (CurrentEncoder != null) - { - switch (CurrentEncoderType) - { - case EncoderType.Blit: - BlitEncoder.EndEncoding(); - CurrentEncoder = null; - break; - case EncoderType.Compute: - ComputeEncoder.EndEncoding(); - CurrentEncoder = null; - break; - case EncoderType.Render: - RenderEncoder.EndEncoding(); - CurrentEncoder = null; - break; - default: - throw new InvalidOperationException(); - } - - CurrentEncoderType = EncoderType.None; - } - } - - private MTLRenderCommandEncoder BeginRenderPass() - { - EndCurrentPass(); - - MTLRenderCommandEncoder renderCommandEncoder = _encoderFactory.CreateRenderCommandEncoder(); - - CurrentEncoder = renderCommandEncoder; - CurrentEncoderType = EncoderType.Render; - - return renderCommandEncoder; - } - - private MTLBlitCommandEncoder BeginBlitPass() - { - EndCurrentPass(); - - using MTLBlitPassDescriptor descriptor = new(); - MTLBlitCommandEncoder blitCommandEncoder = _commandBuffer.BlitCommandEncoder(descriptor); - - CurrentEncoder = blitCommandEncoder; - CurrentEncoderType = EncoderType.Blit; - return blitCommandEncoder; - } - - private MTLComputeCommandEncoder BeginComputePass() - { - EndCurrentPass(); - - MTLComputeCommandEncoder computeCommandEncoder = _encoderFactory.CreateComputeCommandEncoder(); - - CurrentEncoder = computeCommandEncoder; - CurrentEncoderType = EncoderType.Compute; - return computeCommandEncoder; - } -} diff --git a/src/Ryujinx.Graphics.Metal/CommandBufferPool.cs b/src/Ryujinx.Graphics.Metal/CommandBufferPool.cs deleted file mode 100644 index d8c35b757..000000000 --- a/src/Ryujinx.Graphics.Metal/CommandBufferPool.cs +++ /dev/null @@ -1,289 +0,0 @@ -using SharpMetal.Metal; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Runtime.Versioning; -using System.Threading; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - class CommandBufferPool : IDisposable - { - public const int MaxCommandBuffers = 16; - - private readonly int _totalCommandBuffers; - private readonly int _totalCommandBuffersMask; - private readonly MTLCommandQueue _queue; - private readonly Thread _owner; - private IEncoderFactory _defaultEncoderFactory; - - public bool OwnedByCurrentThread => _owner == Thread.CurrentThread; - - [SupportedOSPlatform("macos")] - private struct ReservedCommandBuffer - { - public bool InUse; - public bool InConsumption; - public int SubmissionCount; - public MTLCommandBuffer CommandBuffer; - public CommandBufferEncoder Encoders; - public FenceHolder Fence; - - public List Dependants; - public List Waitables; - - public void Use(MTLCommandQueue queue, IEncoderFactory stateManager) - { - MTLCommandBufferDescriptor descriptor = new(); -#if DEBUG - descriptor.ErrorOptions = MTLCommandBufferErrorOption.EncoderExecutionStatus; -#endif - - CommandBuffer = queue.CommandBuffer(descriptor); - Fence = new FenceHolder(CommandBuffer); - - Encoders.Initialize(CommandBuffer, stateManager); - - InUse = true; - } - - public void Initialize() - { - Dependants = []; - Waitables = []; - Encoders = new CommandBufferEncoder(); - } - } - - private readonly ReservedCommandBuffer[] _commandBuffers; - - private readonly int[] _queuedIndexes; - private int _queuedIndexesPtr; - private int _queuedCount; - private int _inUseCount; - - public CommandBufferPool(MTLCommandQueue queue, bool isLight = false) - { - _queue = queue; - _owner = Thread.CurrentThread; - - _totalCommandBuffers = isLight ? 2 : MaxCommandBuffers; - _totalCommandBuffersMask = _totalCommandBuffers - 1; - - _commandBuffers = new ReservedCommandBuffer[_totalCommandBuffers]; - - _queuedIndexes = new int[_totalCommandBuffers]; - _queuedIndexesPtr = 0; - _queuedCount = 0; - } - - public void Initialize(IEncoderFactory encoderFactory) - { - _defaultEncoderFactory = encoderFactory; - - for (int i = 0; i < _totalCommandBuffers; i++) - { - _commandBuffers[i].Initialize(); - WaitAndDecrementRef(i); - } - } - - public void AddDependant(int cbIndex, IAuto dependant) - { - dependant.IncrementReferenceCount(); - _commandBuffers[cbIndex].Dependants.Add(dependant); - } - - public void AddWaitable(MultiFenceHolder waitable) - { - lock (_commandBuffers) - { - for (int i = 0; i < _totalCommandBuffers; i++) - { - ref ReservedCommandBuffer entry = ref _commandBuffers[i]; - - if (entry.InConsumption) - { - AddWaitable(i, waitable); - } - } - } - } - - public void AddInUseWaitable(MultiFenceHolder waitable) - { - lock (_commandBuffers) - { - for (int i = 0; i < _totalCommandBuffers; i++) - { - ref ReservedCommandBuffer entry = ref _commandBuffers[i]; - - if (entry.InUse) - { - AddWaitable(i, waitable); - } - } - } - } - - public void AddWaitable(int cbIndex, MultiFenceHolder waitable) - { - ref ReservedCommandBuffer entry = ref _commandBuffers[cbIndex]; - if (waitable.AddFence(cbIndex, entry.Fence)) - { - entry.Waitables.Add(waitable); - } - } - - public bool IsFenceOnRentedCommandBuffer(FenceHolder fence) - { - lock (_commandBuffers) - { - for (int i = 0; i < _totalCommandBuffers; i++) - { - ref ReservedCommandBuffer entry = ref _commandBuffers[i]; - - if (entry.InUse && entry.Fence == fence) - { - return true; - } - } - } - - return false; - } - - public FenceHolder GetFence(int cbIndex) - { - return _commandBuffers[cbIndex].Fence; - } - - public int GetSubmissionCount(int cbIndex) - { - return _commandBuffers[cbIndex].SubmissionCount; - } - - private int FreeConsumed(bool wait) - { - int freeEntry = 0; - - while (_queuedCount > 0) - { - int index = _queuedIndexes[_queuedIndexesPtr]; - - ref ReservedCommandBuffer entry = ref _commandBuffers[index]; - - if (wait || !entry.InConsumption || entry.Fence.IsSignaled()) - { - WaitAndDecrementRef(index); - - wait = false; - freeEntry = index; - - _queuedCount--; - _queuedIndexesPtr = (_queuedIndexesPtr + 1) % _totalCommandBuffers; - } - else - { - break; - } - } - - return freeEntry; - } - - public CommandBufferScoped ReturnAndRent(CommandBufferScoped cbs) - { - Return(cbs); - return Rent(); - } - - public CommandBufferScoped Rent() - { - lock (_commandBuffers) - { - int cursor = FreeConsumed(_inUseCount + _queuedCount == _totalCommandBuffers); - - for (int i = 0; i < _totalCommandBuffers; i++) - { - ref ReservedCommandBuffer entry = ref _commandBuffers[cursor]; - - if (!entry.InUse && !entry.InConsumption) - { - entry.Use(_queue, _defaultEncoderFactory); - - _inUseCount++; - - return new CommandBufferScoped(this, entry.CommandBuffer, entry.Encoders, cursor); - } - - cursor = (cursor + 1) & _totalCommandBuffersMask; - } - } - - throw new InvalidOperationException($"Out of command buffers (In use: {_inUseCount}, queued: {_queuedCount}, total: {_totalCommandBuffers})"); - } - - public void Return(CommandBufferScoped cbs) - { - // Ensure the encoder is committed. - cbs.Encoders.EndCurrentPass(); - - lock (_commandBuffers) - { - int cbIndex = cbs.CommandBufferIndex; - - ref ReservedCommandBuffer entry = ref _commandBuffers[cbIndex]; - - Debug.Assert(entry.InUse); - Debug.Assert(entry.CommandBuffer.NativePtr == cbs.CommandBuffer.NativePtr); - entry.InUse = false; - entry.InConsumption = true; - entry.SubmissionCount++; - _inUseCount--; - - MTLCommandBuffer commandBuffer = entry.CommandBuffer; - commandBuffer.Commit(); - - int ptr = (_queuedIndexesPtr + _queuedCount) % _totalCommandBuffers; - _queuedIndexes[ptr] = cbIndex; - _queuedCount++; - } - } - - private void WaitAndDecrementRef(int cbIndex) - { - ref ReservedCommandBuffer entry = ref _commandBuffers[cbIndex]; - - if (entry.InConsumption) - { - entry.Fence.Wait(); - entry.InConsumption = false; - } - - foreach (IAuto dependant in entry.Dependants) - { - dependant.DecrementReferenceCount(cbIndex); - } - - foreach (MultiFenceHolder waitable in entry.Waitables) - { - waitable.RemoveFence(cbIndex); - waitable.RemoveBufferUses(cbIndex); - } - - entry.Dependants.Clear(); - entry.Waitables.Clear(); - entry.Fence?.Dispose(); - } - - public void Dispose() - { - for (int i = 0; i < _totalCommandBuffers; i++) - { - WaitAndDecrementRef(i); - } - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/CommandBufferScoped.cs b/src/Ryujinx.Graphics.Metal/CommandBufferScoped.cs deleted file mode 100644 index 822f69b46..000000000 --- a/src/Ryujinx.Graphics.Metal/CommandBufferScoped.cs +++ /dev/null @@ -1,43 +0,0 @@ -using SharpMetal.Metal; -using System; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - readonly struct CommandBufferScoped : IDisposable - { - private readonly CommandBufferPool _pool; - public MTLCommandBuffer CommandBuffer { get; } - public CommandBufferEncoder Encoders { get; } - public int CommandBufferIndex { get; } - - public CommandBufferScoped(CommandBufferPool pool, MTLCommandBuffer commandBuffer, CommandBufferEncoder encoders, int commandBufferIndex) - { - _pool = pool; - CommandBuffer = commandBuffer; - Encoders = encoders; - CommandBufferIndex = commandBufferIndex; - } - - public void AddDependant(IAuto dependant) - { - _pool.AddDependant(CommandBufferIndex, dependant); - } - - public void AddWaitable(MultiFenceHolder waitable) - { - _pool.AddWaitable(CommandBufferIndex, waitable); - } - - public FenceHolder GetFence() - { - return _pool.GetFence(CommandBufferIndex); - } - - public void Dispose() - { - _pool?.Return(this); - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/Constants.cs b/src/Ryujinx.Graphics.Metal/Constants.cs deleted file mode 100644 index 43baf722a..000000000 --- a/src/Ryujinx.Graphics.Metal/Constants.cs +++ /dev/null @@ -1,41 +0,0 @@ -namespace Ryujinx.Graphics.Metal -{ - static class Constants - { - public const int MaxShaderStages = 5; - public const int MaxVertexBuffers = 16; - public const int MaxUniformBuffersPerStage = 18; - public const int MaxStorageBuffersPerStage = 16; - public const int MaxTexturesPerStage = 64; - public const int MaxImagesPerStage = 16; - - public const int MaxUniformBufferBindings = MaxUniformBuffersPerStage * MaxShaderStages; - public const int MaxStorageBufferBindings = MaxStorageBuffersPerStage * MaxShaderStages; - public const int MaxTextureBindings = MaxTexturesPerStage * MaxShaderStages; - public const int MaxImageBindings = MaxImagesPerStage * MaxShaderStages; - public const int MaxColorAttachments = 8; - public const int MaxViewports = 16; - // TODO: Check this value - public const int MaxVertexAttributes = 31; - - public const int MinResourceAlignment = 16; - - // Must match constants set in shader generation - public const uint ZeroBufferIndex = MaxVertexBuffers; - public const uint BaseSetIndex = MaxVertexBuffers + 1; - - public const uint ConstantBuffersIndex = BaseSetIndex; - public const uint StorageBuffersIndex = BaseSetIndex + 1; - public const uint TexturesIndex = BaseSetIndex + 2; - public const uint ImagesIndex = BaseSetIndex + 3; - - public const uint ConstantBuffersSetIndex = 0; - public const uint StorageBuffersSetIndex = 1; - public const uint TexturesSetIndex = 2; - public const uint ImagesSetIndex = 3; - - public const uint MaximumBufferArgumentTableEntries = 31; - - public const uint MaximumExtraSets = MaximumBufferArgumentTableEntries - ImagesIndex; - } -} diff --git a/src/Ryujinx.Graphics.Metal/CounterEvent.cs b/src/Ryujinx.Graphics.Metal/CounterEvent.cs deleted file mode 100644 index 46b04997e..000000000 --- a/src/Ryujinx.Graphics.Metal/CounterEvent.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Ryujinx.Graphics.GAL; - -namespace Ryujinx.Graphics.Metal -{ - class CounterEvent : ICounterEvent - { - public CounterEvent() - { - Invalid = false; - } - - public bool Invalid { get; set; } - public bool ReserveForHostAccess() - { - return true; - } - - public void Flush() { } - - public void Dispose() { } - } -} diff --git a/src/Ryujinx.Graphics.Metal/DepthStencilCache.cs b/src/Ryujinx.Graphics.Metal/DepthStencilCache.cs deleted file mode 100644 index 47d996010..000000000 --- a/src/Ryujinx.Graphics.Metal/DepthStencilCache.cs +++ /dev/null @@ -1,68 +0,0 @@ -using Ryujinx.Graphics.Metal.State; -using SharpMetal.Metal; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - class DepthStencilCache : StateCache - { - private readonly MTLDevice _device; - - public DepthStencilCache(MTLDevice device) - { - _device = device; - } - - protected override DepthStencilUid GetHash(DepthStencilUid descriptor) - { - return descriptor; - } - - protected override MTLDepthStencilState CreateValue(DepthStencilUid descriptor) - { - // Create descriptors - - ref StencilUid frontUid = ref descriptor.FrontFace; - - using MTLStencilDescriptor frontFaceStencil = new() - { - StencilFailureOperation = frontUid.StencilFailureOperation, - DepthFailureOperation = frontUid.DepthFailureOperation, - DepthStencilPassOperation = frontUid.DepthStencilPassOperation, - StencilCompareFunction = frontUid.StencilCompareFunction, - ReadMask = frontUid.ReadMask, - WriteMask = frontUid.WriteMask - }; - - ref StencilUid backUid = ref descriptor.BackFace; - - using MTLStencilDescriptor backFaceStencil = new() - { - StencilFailureOperation = backUid.StencilFailureOperation, - DepthFailureOperation = backUid.DepthFailureOperation, - DepthStencilPassOperation = backUid.DepthStencilPassOperation, - StencilCompareFunction = backUid.StencilCompareFunction, - ReadMask = backUid.ReadMask, - WriteMask = backUid.WriteMask - }; - - MTLDepthStencilDescriptor mtlDescriptor = new() - { - DepthCompareFunction = descriptor.DepthCompareFunction, - DepthWriteEnabled = descriptor.DepthWriteEnabled - }; - - if (descriptor.StencilTestEnabled) - { - mtlDescriptor.BackFaceStencil = backFaceStencil; - mtlDescriptor.FrontFaceStencil = frontFaceStencil; - } - - using (mtlDescriptor) - { - return _device.NewDepthStencilState(mtlDescriptor); - } - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/DisposableBuffer.cs b/src/Ryujinx.Graphics.Metal/DisposableBuffer.cs deleted file mode 100644 index a2d2247c4..000000000 --- a/src/Ryujinx.Graphics.Metal/DisposableBuffer.cs +++ /dev/null @@ -1,26 +0,0 @@ -using SharpMetal.Metal; -using System; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - readonly struct DisposableBuffer : IDisposable - { - public MTLBuffer Value { get; } - - public DisposableBuffer(MTLBuffer buffer) - { - Value = buffer; - } - - public void Dispose() - { - if (Value != IntPtr.Zero) - { - Value.SetPurgeableState(MTLPurgeableState.Empty); - Value.Dispose(); - } - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/DisposableSampler.cs b/src/Ryujinx.Graphics.Metal/DisposableSampler.cs deleted file mode 100644 index ba041be89..000000000 --- a/src/Ryujinx.Graphics.Metal/DisposableSampler.cs +++ /dev/null @@ -1,22 +0,0 @@ -using SharpMetal.Metal; -using System; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - readonly struct DisposableSampler : IDisposable - { - public MTLSamplerState Value { get; } - - public DisposableSampler(MTLSamplerState sampler) - { - Value = sampler; - } - - public void Dispose() - { - Value.Dispose(); - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/Effects/IPostProcessingEffect.cs b/src/Ryujinx.Graphics.Metal/Effects/IPostProcessingEffect.cs deleted file mode 100644 index d575d521f..000000000 --- a/src/Ryujinx.Graphics.Metal/Effects/IPostProcessingEffect.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; - -namespace Ryujinx.Graphics.Metal.Effects -{ - internal interface IPostProcessingEffect : IDisposable - { - const int LocalGroupSize = 64; - Texture Run(Texture view, int width, int height); - } -} diff --git a/src/Ryujinx.Graphics.Metal/Effects/IScalingFilter.cs b/src/Ryujinx.Graphics.Metal/Effects/IScalingFilter.cs deleted file mode 100644 index 19f1a3c3d..000000000 --- a/src/Ryujinx.Graphics.Metal/Effects/IScalingFilter.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Ryujinx.Graphics.GAL; -using System; - -namespace Ryujinx.Graphics.Metal.Effects -{ - internal interface IScalingFilter : IDisposable - { - float Level { get; set; } - void Run( - Texture view, - Texture destinationTexture, - Format format, - int width, - int height, - Extents2D source, - Extents2D destination); - } -} diff --git a/src/Ryujinx.Graphics.Metal/EncoderResources.cs b/src/Ryujinx.Graphics.Metal/EncoderResources.cs deleted file mode 100644 index 8b856c1ce..000000000 --- a/src/Ryujinx.Graphics.Metal/EncoderResources.cs +++ /dev/null @@ -1,63 +0,0 @@ -using SharpMetal.Metal; -using System.Collections.Generic; - -namespace Ryujinx.Graphics.Metal -{ - public struct RenderEncoderBindings - { - public List Resources = []; - public List VertexBuffers = []; - public List FragmentBuffers = []; - - public RenderEncoderBindings() { } - - public readonly void Clear() - { - Resources.Clear(); - VertexBuffers.Clear(); - FragmentBuffers.Clear(); - } - } - - public struct ComputeEncoderBindings - { - public List Resources = []; - public List Buffers = []; - - public ComputeEncoderBindings() { } - - public readonly void Clear() - { - Resources.Clear(); - Buffers.Clear(); - } - } - - public struct BufferResource - { - public MTLBuffer Buffer; - public ulong Offset; - public ulong Binding; - - public BufferResource(MTLBuffer buffer, ulong offset, ulong binding) - { - Buffer = buffer; - Offset = offset; - Binding = binding; - } - } - - public struct Resource - { - public MTLResource MtlResource; - public MTLResourceUsage ResourceUsage; - public MTLRenderStages Stages; - - public Resource(MTLResource resource, MTLResourceUsage resourceUsage, MTLRenderStages stages) - { - MtlResource = resource; - ResourceUsage = resourceUsage; - Stages = stages; - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/EncoderState.cs b/src/Ryujinx.Graphics.Metal/EncoderState.cs deleted file mode 100644 index 64c50d71b..000000000 --- a/src/Ryujinx.Graphics.Metal/EncoderState.cs +++ /dev/null @@ -1,206 +0,0 @@ -using Ryujinx.Common.Memory; -using Ryujinx.Graphics.GAL; -using Ryujinx.Graphics.Metal.State; -using Ryujinx.Graphics.Shader; -using SharpMetal.Metal; -using System; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - [Flags] - enum DirtyFlags - { - None = 0, - RenderPipeline = 1 << 0, - ComputePipeline = 1 << 1, - DepthStencil = 1 << 2, - DepthClamp = 1 << 3, - DepthBias = 1 << 4, - CullMode = 1 << 5, - FrontFace = 1 << 6, - StencilRef = 1 << 7, - Viewports = 1 << 8, - Scissors = 1 << 9, - Uniforms = 1 << 10, - Storages = 1 << 11, - Textures = 1 << 12, - Images = 1 << 13, - - ArgBuffers = Uniforms | Storages | Textures | Images, - - RenderAll = RenderPipeline | DepthStencil | DepthClamp | DepthBias | CullMode | FrontFace | StencilRef | Viewports | Scissors | ArgBuffers, - ComputeAll = ComputePipeline | ArgBuffers, - All = RenderAll | ComputeAll, - } - - record struct BufferRef - { - public Auto Buffer; - public BufferRange? Range; - - public BufferRef(Auto buffer) - { - Buffer = buffer; - } - - public BufferRef(Auto buffer, ref BufferRange range) - { - Buffer = buffer; - Range = range; - } - } - - record struct TextureRef - { - public ShaderStage Stage; - public TextureBase Storage; - public Auto Sampler; - public Format ImageFormat; - - public TextureRef(ShaderStage stage, TextureBase storage, Auto sampler) - { - Stage = stage; - Storage = storage; - Sampler = sampler; - } - } - - record struct ImageRef - { - public ShaderStage Stage; - public Texture Storage; - - public ImageRef(ShaderStage stage, Texture storage) - { - Stage = stage; - Storage = storage; - } - } - - struct PredrawState - { - public MTLCullMode CullMode; - public DepthStencilUid DepthStencilUid; - public PrimitiveTopology Topology; - public MTLViewport[] Viewports; - } - - struct RenderTargetCopy - { - public MTLScissorRect[] Scissors; - public Texture DepthStencil; - public Texture[] RenderTargets; - } - - [SupportedOSPlatform("macos")] - class EncoderState - { - public Program RenderProgram = null; - public Program ComputeProgram = null; - - public PipelineState Pipeline; - public DepthStencilUid DepthStencilUid; - - public readonly record struct ArrayRef(ShaderStage Stage, T Array); - - public readonly BufferRef[] UniformBufferRefs = new BufferRef[Constants.MaxUniformBufferBindings]; - public readonly BufferRef[] StorageBufferRefs = new BufferRef[Constants.MaxStorageBufferBindings]; - public readonly TextureRef[] TextureRefs = new TextureRef[Constants.MaxTextureBindings * 2]; - public readonly ImageRef[] ImageRefs = new ImageRef[Constants.MaxImageBindings * 2]; - - public ArrayRef[] TextureArrayRefs = []; - public ArrayRef[] ImageArrayRefs = []; - - public ArrayRef[] TextureArrayExtraRefs = []; - public ArrayRef[] ImageArrayExtraRefs = []; - - public IndexBufferState IndexBuffer = default; - - public MTLDepthClipMode DepthClipMode = MTLDepthClipMode.Clip; - - public float DepthBias; - public float SlopeScale; - public float Clamp; - - public int BackRefValue = 0; - public int FrontRefValue = 0; - - public PrimitiveTopology Topology = PrimitiveTopology.Triangles; - public MTLCullMode CullMode = MTLCullMode.None; - public MTLWinding Winding = MTLWinding.CounterClockwise; - public bool CullBoth = false; - - public MTLViewport[] Viewports = new MTLViewport[Constants.MaxViewports]; - public MTLScissorRect[] Scissors = new MTLScissorRect[Constants.MaxViewports]; - - // Changes to attachments take recreation! - public Texture DepthStencil; - public Texture[] RenderTargets = new Texture[Constants.MaxColorAttachments]; - public ITexture PreMaskDepthStencil = default; - public ITexture[] PreMaskRenderTargets; - public bool FramebufferUsingColorWriteMask; - - public Array8 StoredBlend; - public ColorF BlendColor = new(); - - public readonly VertexBufferState[] VertexBuffers = new VertexBufferState[Constants.MaxVertexBuffers]; - public readonly VertexAttribDescriptor[] VertexAttribs = new VertexAttribDescriptor[Constants.MaxVertexAttributes]; - // Dirty flags - public DirtyFlags Dirty = DirtyFlags.None; - - // Only to be used for present - public bool ClearLoadAction = false; - - public RenderEncoderBindings RenderEncoderBindings = new(); - public ComputeEncoderBindings ComputeEncoderBindings = new(); - - public EncoderState() - { - Pipeline.Initialize(); - DepthStencilUid.DepthCompareFunction = MTLCompareFunction.Always; - } - - public RenderTargetCopy InheritForClear(EncoderState other, bool depth, int singleIndex = -1) - { - // Inherit render target related information without causing a render encoder split. - - RenderTargetCopy oldState = new() - { - Scissors = other.Scissors, - RenderTargets = other.RenderTargets, - DepthStencil = other.DepthStencil - }; - - Scissors = other.Scissors; - RenderTargets = other.RenderTargets; - DepthStencil = other.DepthStencil; - - Pipeline.ColorBlendAttachmentStateCount = other.Pipeline.ColorBlendAttachmentStateCount; - Pipeline.Internal.ColorBlendState = other.Pipeline.Internal.ColorBlendState; - Pipeline.DepthStencilFormat = other.Pipeline.DepthStencilFormat; - - ref Array8 blendStates = ref Pipeline.Internal.ColorBlendState; - - // Mask out irrelevant attachments. - for (int i = 0; i < blendStates.Length; i++) - { - if (depth || (singleIndex != -1 && singleIndex != i)) - { - blendStates[i].WriteMask = MTLColorWriteMask.None; - } - } - - return oldState; - } - - public void Restore(RenderTargetCopy copy) - { - Scissors = copy.Scissors; - RenderTargets = copy.RenderTargets; - DepthStencil = copy.DepthStencil; - - Pipeline.Internal.ResetColorState(); - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs deleted file mode 100644 index 7901e5a52..000000000 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ /dev/null @@ -1,1790 +0,0 @@ -using Ryujinx.Common.Logging; -using Ryujinx.Common.Memory; -using Ryujinx.Graphics.GAL; -using Ryujinx.Graphics.Metal.State; -using Ryujinx.Graphics.Shader; -using SharpMetal.Metal; -using System; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Runtime.Versioning; -using BufferAssignment = Ryujinx.Graphics.GAL.BufferAssignment; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - struct EncoderStateManager : IDisposable - { - private const int ArrayGrowthSize = 16; - - private readonly MTLDevice _device; - private readonly Pipeline _pipeline; - private readonly BufferManager _bufferManager; - - private readonly DepthStencilCache _depthStencilCache; - private readonly MTLDepthStencilState _defaultState; - - private readonly EncoderState _mainState = new(); - private EncoderState _currentState; - - public readonly IndexBufferState IndexBuffer => _currentState.IndexBuffer; - public readonly PrimitiveTopology Topology => _currentState.Topology; - public readonly Texture[] RenderTargets => _currentState.RenderTargets; - public readonly Texture DepthStencil => _currentState.DepthStencil; - public readonly ComputeSize ComputeLocalSize => _currentState.ComputeProgram.ComputeLocalSize; - - // RGBA32F is the biggest format - private const int ZeroBufferSize = 4 * 4; - private readonly BufferHandle _zeroBuffer; - - public unsafe EncoderStateManager(MTLDevice device, BufferManager bufferManager, Pipeline pipeline) - { - _device = device; - _pipeline = pipeline; - _bufferManager = bufferManager; - - _depthStencilCache = new(device); - _currentState = _mainState; - - _defaultState = _depthStencilCache.GetOrCreate(_currentState.DepthStencilUid); - - // Zero buffer - byte[] zeros = new byte[ZeroBufferSize]; - fixed (byte* ptr = zeros) - { - _zeroBuffer = _bufferManager.Create((IntPtr)ptr, ZeroBufferSize); - } - } - - public readonly void Dispose() - { - _depthStencilCache.Dispose(); - } - - private readonly void SignalDirty(DirtyFlags flags) - { - _currentState.Dirty |= flags; - } - - public readonly void SignalRenderDirty() - { - SignalDirty(DirtyFlags.RenderAll); - } - - public readonly void SignalComputeDirty() - { - SignalDirty(DirtyFlags.ComputeAll); - } - - public EncoderState SwapState(EncoderState state, DirtyFlags flags = DirtyFlags.All) - { - _currentState = state ?? _mainState; - - SignalDirty(flags); - - return _mainState; - } - - public PredrawState SavePredrawState() - { - return new PredrawState - { - CullMode = _currentState.CullMode, - DepthStencilUid = _currentState.DepthStencilUid, - Topology = _currentState.Topology, - Viewports = _currentState.Viewports.ToArray(), - }; - } - - public readonly void RestorePredrawState(PredrawState state) - { - _currentState.CullMode = state.CullMode; - _currentState.DepthStencilUid = state.DepthStencilUid; - _currentState.Topology = state.Topology; - _currentState.Viewports = state.Viewports; - - SignalDirty(DirtyFlags.CullMode | DirtyFlags.DepthStencil | DirtyFlags.Viewports); - } - - public readonly void SetClearLoadAction(bool clear) - { - _currentState.ClearLoadAction = clear; - } - - public readonly void DirtyTextures() - { - SignalDirty(DirtyFlags.Textures); - } - - public readonly void DirtyImages() - { - SignalDirty(DirtyFlags.Images); - } - - public readonly MTLRenderCommandEncoder CreateRenderCommandEncoder() - { - // Initialise Pass & State - using MTLRenderPassDescriptor renderPassDescriptor = new(); - - for (int i = 0; i < Constants.MaxColorAttachments; i++) - { - if (_currentState.RenderTargets[i] is Texture tex) - { - MTLRenderPassColorAttachmentDescriptor passAttachment = renderPassDescriptor.ColorAttachments.Object((ulong)i); - tex.PopulateRenderPassAttachment(passAttachment); - passAttachment.LoadAction = _currentState.ClearLoadAction ? MTLLoadAction.Clear : MTLLoadAction.Load; - passAttachment.StoreAction = MTLStoreAction.Store; - } - } - - MTLRenderPassDepthAttachmentDescriptor depthAttachment = renderPassDescriptor.DepthAttachment; - MTLRenderPassStencilAttachmentDescriptor stencilAttachment = renderPassDescriptor.StencilAttachment; - - if (_currentState.DepthStencil != null) - { - switch (_currentState.DepthStencil.GetHandle().PixelFormat) - { - // Depth Only Attachment - case MTLPixelFormat.Depth16Unorm: - case MTLPixelFormat.Depth32Float: - depthAttachment.Texture = _currentState.DepthStencil.GetHandle(); - depthAttachment.LoadAction = MTLLoadAction.Load; - depthAttachment.StoreAction = MTLStoreAction.Store; - break; - - // Stencil Only Attachment - case MTLPixelFormat.Stencil8: - stencilAttachment.Texture = _currentState.DepthStencil.GetHandle(); - stencilAttachment.LoadAction = MTLLoadAction.Load; - stencilAttachment.StoreAction = MTLStoreAction.Store; - break; - - // Combined Attachment - case MTLPixelFormat.Depth24UnormStencil8: - case MTLPixelFormat.Depth32FloatStencil8: - depthAttachment.Texture = _currentState.DepthStencil.GetHandle(); - depthAttachment.LoadAction = MTLLoadAction.Load; - depthAttachment.StoreAction = MTLStoreAction.Store; - - stencilAttachment.Texture = _currentState.DepthStencil.GetHandle(); - stencilAttachment.LoadAction = MTLLoadAction.Load; - stencilAttachment.StoreAction = MTLStoreAction.Store; - break; - default: - Logger.Error?.PrintMsg(LogClass.Gpu, $"Unsupported Depth/Stencil Format: {_currentState.DepthStencil.GetHandle().PixelFormat}!"); - break; - } - } - - // Initialise Encoder - MTLRenderCommandEncoder renderCommandEncoder = _pipeline.CommandBuffer.RenderCommandEncoder(renderPassDescriptor); - - return renderCommandEncoder; - } - - public readonly MTLComputeCommandEncoder CreateComputeCommandEncoder() - { - using MTLComputePassDescriptor descriptor = new(); - MTLComputeCommandEncoder computeCommandEncoder = _pipeline.CommandBuffer.ComputeCommandEncoder(descriptor); - - return computeCommandEncoder; - } - - public readonly void RenderResourcesPrepass() - { - _currentState.RenderEncoderBindings.Clear(); - - if ((_currentState.Dirty & DirtyFlags.RenderPipeline) != 0) - { - SetVertexBuffers(_currentState.VertexBuffers, ref _currentState.RenderEncoderBindings); - } - - if ((_currentState.Dirty & DirtyFlags.Uniforms) != 0) - { - UpdateAndBind(_currentState.RenderProgram, Constants.ConstantBuffersSetIndex, ref _currentState.RenderEncoderBindings); - } - - if ((_currentState.Dirty & DirtyFlags.Storages) != 0) - { - UpdateAndBind(_currentState.RenderProgram, Constants.StorageBuffersSetIndex, ref _currentState.RenderEncoderBindings); - } - - if ((_currentState.Dirty & DirtyFlags.Textures) != 0) - { - UpdateAndBind(_currentState.RenderProgram, Constants.TexturesSetIndex, ref _currentState.RenderEncoderBindings); - } - - if ((_currentState.Dirty & DirtyFlags.Images) != 0) - { - UpdateAndBind(_currentState.RenderProgram, Constants.ImagesSetIndex, ref _currentState.RenderEncoderBindings); - } - } - - public readonly void ComputeResourcesPrepass() - { - _currentState.ComputeEncoderBindings.Clear(); - - if ((_currentState.Dirty & DirtyFlags.Uniforms) != 0) - { - UpdateAndBind(_currentState.ComputeProgram, Constants.ConstantBuffersSetIndex, ref _currentState.ComputeEncoderBindings); - } - - if ((_currentState.Dirty & DirtyFlags.Storages) != 0) - { - UpdateAndBind(_currentState.ComputeProgram, Constants.StorageBuffersSetIndex, ref _currentState.ComputeEncoderBindings); - } - - if ((_currentState.Dirty & DirtyFlags.Textures) != 0) - { - UpdateAndBind(_currentState.ComputeProgram, Constants.TexturesSetIndex, ref _currentState.ComputeEncoderBindings); - } - - if ((_currentState.Dirty & DirtyFlags.Images) != 0) - { - UpdateAndBind(_currentState.ComputeProgram, Constants.ImagesSetIndex, ref _currentState.ComputeEncoderBindings); - } - } - - public void RebindRenderState(MTLRenderCommandEncoder renderCommandEncoder) - { - if ((_currentState.Dirty & DirtyFlags.RenderPipeline) != 0) - { - SetRenderPipelineState(renderCommandEncoder); - } - - if ((_currentState.Dirty & DirtyFlags.DepthStencil) != 0) - { - SetDepthStencilState(renderCommandEncoder); - } - - if ((_currentState.Dirty & DirtyFlags.DepthClamp) != 0) - { - SetDepthClamp(renderCommandEncoder); - } - - if ((_currentState.Dirty & DirtyFlags.DepthBias) != 0) - { - SetDepthBias(renderCommandEncoder); - } - - if ((_currentState.Dirty & DirtyFlags.CullMode) != 0) - { - SetCullMode(renderCommandEncoder); - } - - if ((_currentState.Dirty & DirtyFlags.FrontFace) != 0) - { - SetFrontFace(renderCommandEncoder); - } - - if ((_currentState.Dirty & DirtyFlags.StencilRef) != 0) - { - SetStencilRefValue(renderCommandEncoder); - } - - if ((_currentState.Dirty & DirtyFlags.Viewports) != 0) - { - SetViewports(renderCommandEncoder); - } - - if ((_currentState.Dirty & DirtyFlags.Scissors) != 0) - { - SetScissors(renderCommandEncoder); - } - - foreach (Resource resource in _currentState.RenderEncoderBindings.Resources) - { - renderCommandEncoder.UseResource(resource.MtlResource, resource.ResourceUsage, resource.Stages); - } - - foreach (BufferResource buffer in _currentState.RenderEncoderBindings.VertexBuffers) - { - renderCommandEncoder.SetVertexBuffer(buffer.Buffer, buffer.Offset, buffer.Binding); - } - - foreach (BufferResource buffer in _currentState.RenderEncoderBindings.FragmentBuffers) - { - renderCommandEncoder.SetFragmentBuffer(buffer.Buffer, buffer.Offset, buffer.Binding); - } - - _currentState.Dirty &= ~DirtyFlags.RenderAll; - } - - public readonly void RebindComputeState(MTLComputeCommandEncoder computeCommandEncoder) - { - if ((_currentState.Dirty & DirtyFlags.ComputePipeline) != 0) - { - SetComputePipelineState(computeCommandEncoder); - } - - foreach (Resource resource in _currentState.ComputeEncoderBindings.Resources) - { - computeCommandEncoder.UseResource(resource.MtlResource, resource.ResourceUsage); - } - - foreach (BufferResource buffer in _currentState.ComputeEncoderBindings.Buffers) - { - computeCommandEncoder.SetBuffer(buffer.Buffer, buffer.Offset, buffer.Binding); - } - - _currentState.Dirty &= ~DirtyFlags.ComputeAll; - } - - private readonly void SetRenderPipelineState(MTLRenderCommandEncoder renderCommandEncoder) - { - MTLRenderPipelineState pipelineState = _currentState.Pipeline.CreateRenderPipeline(_device, _currentState.RenderProgram); - - renderCommandEncoder.SetRenderPipelineState(pipelineState); - - renderCommandEncoder.SetBlendColor( - _currentState.BlendColor.Red, - _currentState.BlendColor.Green, - _currentState.BlendColor.Blue, - _currentState.BlendColor.Alpha); - } - - private readonly void SetComputePipelineState(MTLComputeCommandEncoder computeCommandEncoder) - { - if (_currentState.ComputeProgram == null) - { - return; - } - - MTLComputePipelineState pipelineState = PipelineState.CreateComputePipeline(_device, _currentState.ComputeProgram); - - computeCommandEncoder.SetComputePipelineState(pipelineState); - } - - public readonly void UpdateIndexBuffer(BufferRange buffer, IndexType type) - { - if (buffer.Handle != BufferHandle.Null) - { - _currentState.IndexBuffer = new IndexBufferState(buffer.Handle, buffer.Offset, buffer.Size, type); - } - else - { - _currentState.IndexBuffer = IndexBufferState.Null; - } - } - - public readonly void UpdatePrimitiveTopology(PrimitiveTopology topology) - { - _currentState.Topology = topology; - } - - public readonly void UpdateProgram(IProgram program) - { - Program prg = (Program)program; - - if (prg.VertexFunction == IntPtr.Zero && prg.ComputeFunction == IntPtr.Zero) - { - if (prg.FragmentFunction == IntPtr.Zero) - { - Logger.Error?.PrintMsg(LogClass.Gpu, "No compute function"); - } - else - { - Logger.Error?.PrintMsg(LogClass.Gpu, "No vertex function"); - } - return; - } - - if (prg.VertexFunction != IntPtr.Zero) - { - _currentState.RenderProgram = prg; - - SignalDirty(DirtyFlags.RenderPipeline | DirtyFlags.ArgBuffers); - } - else if (prg.ComputeFunction != IntPtr.Zero) - { - _currentState.ComputeProgram = prg; - - SignalDirty(DirtyFlags.ComputePipeline | DirtyFlags.ArgBuffers); - } - } - - public readonly void UpdateRasterizerDiscard(bool discard) - { - _currentState.Pipeline.RasterizerDiscardEnable = discard; - - SignalDirty(DirtyFlags.RenderPipeline); - } - - public readonly void UpdateRenderTargets(ITexture[] colors, ITexture depthStencil) - { - _currentState.FramebufferUsingColorWriteMask = false; - UpdateRenderTargetsInternal(colors, depthStencil); - } - - public readonly void UpdateRenderTargetColorMasks(ReadOnlySpan componentMask) - { - ref Array8 blendState = ref _currentState.Pipeline.Internal.ColorBlendState; - - for (int i = 0; i < componentMask.Length; i++) - { - bool red = (componentMask[i] & (0x1 << 0)) != 0; - bool green = (componentMask[i] & (0x1 << 1)) != 0; - bool blue = (componentMask[i] & (0x1 << 2)) != 0; - bool alpha = (componentMask[i] & (0x1 << 3)) != 0; - - MTLColorWriteMask mask = MTLColorWriteMask.None; - - mask |= red ? MTLColorWriteMask.Red : 0; - mask |= green ? MTLColorWriteMask.Green : 0; - mask |= blue ? MTLColorWriteMask.Blue : 0; - mask |= alpha ? MTLColorWriteMask.Alpha : 0; - - ref ColorBlendStateUid mtlBlend = ref blendState[i]; - - // When color write mask is 0, remove all blend state to help the pipeline cache. - // Restore it when the mask becomes non-zero. - if (mtlBlend.WriteMask != mask) - { - if (mask == 0) - { - _currentState.StoredBlend[i] = mtlBlend; - - mtlBlend.Swap(new ColorBlendStateUid()); - } - else if (mtlBlend.WriteMask == 0) - { - mtlBlend.Swap(_currentState.StoredBlend[i]); - } - } - - blendState[i].WriteMask = mask; - } - - if (_currentState.FramebufferUsingColorWriteMask) - { - UpdateRenderTargetsInternal(_currentState.PreMaskRenderTargets, _currentState.PreMaskDepthStencil); - } - else - { - // Requires recreating pipeline - if (_pipeline.CurrentEncoderType == EncoderType.Render) - { - _pipeline.EndCurrentPass(); - } - } - } - - private readonly void UpdateRenderTargetsInternal(ITexture[] colors, ITexture depthStencil) - { - // TBDR GPUs don't work properly if the same attachment is bound to multiple targets, - // due to each attachment being a copy of the real attachment, rather than a direct write. - // - // Just try to remove duplicate attachments. - // Save a copy of the array to rebind when mask changes. - - // Look for textures that are masked out. - - ref PipelineState pipeline = ref _currentState.Pipeline; - ref Array8 blendState = ref pipeline.Internal.ColorBlendState; - - pipeline.ColorBlendAttachmentStateCount = (uint)colors.Length; - - for (int i = 0; i < colors.Length; i++) - { - if (colors[i] == null) - { - continue; - } - - MTLColorWriteMask mtlMask = blendState[i].WriteMask; - - for (int j = 0; j < i; j++) - { - // Check each binding for a duplicate binding before it. - - if (colors[i] == colors[j]) - { - // Prefer the binding with no write mask. - - MTLColorWriteMask mtlMask2 = blendState[j].WriteMask; - - if (mtlMask == 0) - { - colors[i] = null; - MaskOut(colors, depthStencil); - } - else if (mtlMask2 == 0) - { - colors[j] = null; - MaskOut(colors, depthStencil); - } - } - } - } - - _currentState.RenderTargets = new Texture[Constants.MaxColorAttachments]; - - for (int i = 0; i < colors.Length; i++) - { - if (colors[i] is not Texture tex) - { - blendState[i].PixelFormat = MTLPixelFormat.Invalid; - - continue; - } - - blendState[i].PixelFormat = tex.GetHandle().PixelFormat; // TODO: cache this - _currentState.RenderTargets[i] = tex; - } - - if (depthStencil is Texture depthTexture) - { - pipeline.DepthStencilFormat = depthTexture.GetHandle().PixelFormat; // TODO: cache this - _currentState.DepthStencil = depthTexture; - } - else if (depthStencil == null) - { - pipeline.DepthStencilFormat = MTLPixelFormat.Invalid; - _currentState.DepthStencil = null; - } - - // Requires recreating pipeline - if (_pipeline.CurrentEncoderType == EncoderType.Render) - { - _pipeline.EndCurrentPass(); - } - } - - private readonly void MaskOut(ITexture[] colors, ITexture depthStencil) - { - if (!_currentState.FramebufferUsingColorWriteMask) - { - _currentState.PreMaskRenderTargets = colors; - _currentState.PreMaskDepthStencil = depthStencil; - } - - // If true, then the framebuffer must be recreated when the mask changes. - _currentState.FramebufferUsingColorWriteMask = true; - } - - public readonly void UpdateVertexAttribs(ReadOnlySpan vertexAttribs) - { - vertexAttribs.CopyTo(_currentState.VertexAttribs); - - // Update the buffers on the pipeline - UpdatePipelineVertexState(_currentState.VertexBuffers, _currentState.VertexAttribs); - - SignalDirty(DirtyFlags.RenderPipeline); - } - - public readonly void UpdateBlendDescriptors(int index, BlendDescriptor blend) - { - ref ColorBlendStateUid blendState = ref _currentState.Pipeline.Internal.ColorBlendState[index]; - - blendState.Enable = blend.Enable; - blendState.AlphaBlendOperation = blend.AlphaOp.Convert(); - blendState.RgbBlendOperation = blend.ColorOp.Convert(); - blendState.SourceAlphaBlendFactor = blend.AlphaSrcFactor.Convert(); - blendState.DestinationAlphaBlendFactor = blend.AlphaDstFactor.Convert(); - blendState.SourceRGBBlendFactor = blend.ColorSrcFactor.Convert(); - blendState.DestinationRGBBlendFactor = blend.ColorDstFactor.Convert(); - - if (blendState.WriteMask == 0) - { - _currentState.StoredBlend[index] = blendState; - - blendState.Swap(new ColorBlendStateUid()); - } - - _currentState.BlendColor = blend.BlendConstant; - - SignalDirty(DirtyFlags.RenderPipeline); - } - - public void UpdateStencilState(StencilTestDescriptor stencilTest) - { - ref DepthStencilUid uid = ref _currentState.DepthStencilUid; - - uid.FrontFace = new StencilUid - { - StencilFailureOperation = stencilTest.FrontSFail.Convert(), - DepthFailureOperation = stencilTest.FrontDpFail.Convert(), - DepthStencilPassOperation = stencilTest.FrontDpPass.Convert(), - StencilCompareFunction = stencilTest.FrontFunc.Convert(), - ReadMask = (uint)stencilTest.FrontFuncMask, - WriteMask = (uint)stencilTest.FrontMask - }; - - uid.BackFace = new StencilUid - { - StencilFailureOperation = stencilTest.BackSFail.Convert(), - DepthFailureOperation = stencilTest.BackDpFail.Convert(), - DepthStencilPassOperation = stencilTest.BackDpPass.Convert(), - StencilCompareFunction = stencilTest.BackFunc.Convert(), - ReadMask = (uint)stencilTest.BackFuncMask, - WriteMask = (uint)stencilTest.BackMask - }; - - uid.StencilTestEnabled = stencilTest.TestEnable; - - UpdateStencilRefValue(stencilTest.FrontFuncRef, stencilTest.BackFuncRef); - - SignalDirty(DirtyFlags.DepthStencil); - } - - public readonly void UpdateDepthState(DepthTestDescriptor depthTest) - { - ref DepthStencilUid uid = ref _currentState.DepthStencilUid; - - uid.DepthCompareFunction = depthTest.TestEnable ? depthTest.Func.Convert() : MTLCompareFunction.Always; - uid.DepthWriteEnabled = depthTest.TestEnable && depthTest.WriteEnable; - - SignalDirty(DirtyFlags.DepthStencil); - } - - public readonly void UpdateDepthClamp(bool clamp) - { - _currentState.DepthClipMode = clamp ? MTLDepthClipMode.Clamp : MTLDepthClipMode.Clip; - - // Inline update - if (_pipeline.Encoders.TryGetRenderEncoder(out MTLRenderCommandEncoder renderCommandEncoder)) - { - SetDepthClamp(renderCommandEncoder); - return; - } - - SignalDirty(DirtyFlags.DepthClamp); - } - - public readonly void UpdateDepthBias(float depthBias, float slopeScale, float clamp) - { - _currentState.DepthBias = depthBias; - _currentState.SlopeScale = slopeScale; - _currentState.Clamp = clamp; - - // Inline update - if (_pipeline.Encoders.TryGetRenderEncoder(out MTLRenderCommandEncoder renderCommandEncoder)) - { - SetDepthBias(renderCommandEncoder); - return; - } - - SignalDirty(DirtyFlags.DepthBias); - } - - public readonly void UpdateLogicOpState(bool enable, LogicalOp op) - { - _currentState.Pipeline.LogicOpEnable = enable; - _currentState.Pipeline.LogicOp = op.Convert(); - - SignalDirty(DirtyFlags.RenderPipeline); - } - - public readonly void UpdateMultisampleState(MultisampleDescriptor multisample) - { - _currentState.Pipeline.AlphaToCoverageEnable = multisample.AlphaToCoverageEnable; - _currentState.Pipeline.AlphaToOneEnable = multisample.AlphaToOneEnable; - - SignalDirty(DirtyFlags.RenderPipeline); - } - - public void UpdateScissors(ReadOnlySpan> regions) - { - for (int i = 0; i < regions.Length; i++) - { - Rectangle region = regions[i]; - - _currentState.Scissors[i] = new MTLScissorRect - { - height = (ulong)region.Height, - width = (ulong)region.Width, - x = (ulong)region.X, - y = (ulong)region.Y - }; - } - - // Inline update - if (_pipeline.Encoders.TryGetRenderEncoder(out MTLRenderCommandEncoder renderCommandEncoder)) - { - SetScissors(renderCommandEncoder); - return; - } - - SignalDirty(DirtyFlags.Scissors); - } - - public void UpdateViewports(ReadOnlySpan viewports) - { - static float Clamp(float value) - { - return Math.Clamp(value, 0f, 1f); - } - - for (int i = 0; i < viewports.Length; i++) - { - Viewport viewport = viewports[i]; - // Y coordinate is inverted - _currentState.Viewports[i] = new MTLViewport - { - originX = viewport.Region.X, - originY = viewport.Region.Y + viewport.Region.Height, - width = viewport.Region.Width, - height = -viewport.Region.Height, - znear = Clamp(viewport.DepthNear), - zfar = Clamp(viewport.DepthFar) - }; - } - - // Inline update - if (_pipeline.Encoders.TryGetRenderEncoder(out MTLRenderCommandEncoder renderCommandEncoder)) - { - SetViewports(renderCommandEncoder); - return; - } - - SignalDirty(DirtyFlags.Viewports); - } - - public readonly void UpdateVertexBuffers(ReadOnlySpan vertexBuffers) - { - for (int i = 0; i < Constants.MaxVertexBuffers; i++) - { - if (i < vertexBuffers.Length) - { - VertexBufferDescriptor vertexBuffer = vertexBuffers[i]; - - _currentState.VertexBuffers[i] = new VertexBufferState( - vertexBuffer.Buffer.Handle, - vertexBuffer.Buffer.Offset, - vertexBuffer.Buffer.Size, - vertexBuffer.Divisor, - vertexBuffer.Stride); - } - else - { - _currentState.VertexBuffers[i] = VertexBufferState.Null; - } - } - - // Update the buffers on the pipeline - UpdatePipelineVertexState(_currentState.VertexBuffers, _currentState.VertexAttribs); - - SignalDirty(DirtyFlags.RenderPipeline); - } - - public readonly void UpdateUniformBuffers(ReadOnlySpan buffers) - { - foreach (BufferAssignment assignment in buffers) - { - BufferRange buffer = assignment.Range; - int index = assignment.Binding; - - Auto mtlBuffer = buffer.Handle == BufferHandle.Null - ? null - : _bufferManager.GetBuffer(buffer.Handle, buffer.Write); - - _currentState.UniformBufferRefs[index] = new BufferRef(mtlBuffer, ref buffer); - } - - SignalDirty(DirtyFlags.Uniforms); - } - - public readonly void UpdateStorageBuffers(ReadOnlySpan buffers) - { - foreach (BufferAssignment assignment in buffers) - { - BufferRange buffer = assignment.Range; - int index = assignment.Binding; - - Auto mtlBuffer = buffer.Handle == BufferHandle.Null - ? null - : _bufferManager.GetBuffer(buffer.Handle, buffer.Write); - - _currentState.StorageBufferRefs[index] = new BufferRef(mtlBuffer, ref buffer); - } - - SignalDirty(DirtyFlags.Storages); - } - - public readonly void UpdateStorageBuffers(int first, ReadOnlySpan> buffers) - { - for (int i = 0; i < buffers.Length; i++) - { - Auto mtlBuffer = buffers[i]; - int index = first + i; - - _currentState.StorageBufferRefs[index] = new BufferRef(mtlBuffer); - } - - SignalDirty(DirtyFlags.Storages); - } - - public void UpdateCullMode(bool enable, Face face) - { - bool dirtyScissor = (face == Face.FrontAndBack) != _currentState.CullBoth; - - _currentState.CullMode = enable ? face.Convert() : MTLCullMode.None; - _currentState.CullBoth = face == Face.FrontAndBack; - - // Inline update - if (_pipeline.Encoders.TryGetRenderEncoder(out MTLRenderCommandEncoder renderCommandEncoder)) - { - SetCullMode(renderCommandEncoder); - SetScissors(renderCommandEncoder); - return; - } - - // Mark dirty - SignalDirty(DirtyFlags.CullMode); - - if (dirtyScissor) - { - SignalDirty(DirtyFlags.Scissors); - } - } - - public readonly void UpdateFrontFace(FrontFace frontFace) - { - _currentState.Winding = frontFace.Convert(); - - // Inline update - if (_pipeline.Encoders.TryGetRenderEncoder(out MTLRenderCommandEncoder renderCommandEncoder)) - { - SetFrontFace(renderCommandEncoder); - return; - } - - SignalDirty(DirtyFlags.FrontFace); - } - - private readonly void UpdateStencilRefValue(int frontRef, int backRef) - { - _currentState.FrontRefValue = frontRef; - _currentState.BackRefValue = backRef; - - // Inline update - if (_pipeline.Encoders.TryGetRenderEncoder(out MTLRenderCommandEncoder renderCommandEncoder)) - { - SetStencilRefValue(renderCommandEncoder); - } - - SignalDirty(DirtyFlags.StencilRef); - } - - public readonly void UpdateTextureAndSampler(ShaderStage stage, int binding, TextureBase texture, SamplerHolder samplerHolder) - { - if (texture != null) - { - _currentState.TextureRefs[binding] = new(stage, texture, samplerHolder?.GetSampler()); - } - else - { - _currentState.TextureRefs[binding] = default; - } - - SignalDirty(DirtyFlags.Textures); - } - - public readonly void UpdateImage(ShaderStage stage, int binding, TextureBase image) - { - if (image is Texture view) - { - _currentState.ImageRefs[binding] = new(stage, view); - } - else - { - _currentState.ImageRefs[binding] = default; - } - - SignalDirty(DirtyFlags.Images); - } - - public readonly void UpdateTextureArray(ShaderStage stage, int binding, TextureArray array) - { - ref EncoderState.ArrayRef arrayRef = ref GetArrayRef(ref _currentState.TextureArrayRefs, binding, ArrayGrowthSize); - - if (arrayRef.Stage != stage || arrayRef.Array != array) - { - arrayRef = new EncoderState.ArrayRef(stage, array); - - SignalDirty(DirtyFlags.Textures); - } - } - - public readonly void UpdateTextureArraySeparate(ShaderStage stage, int setIndex, TextureArray array) - { - ref EncoderState.ArrayRef arrayRef = ref GetArrayRef(ref _currentState.TextureArrayExtraRefs, setIndex - MetalRenderer.TotalSets); - - if (arrayRef.Stage != stage || arrayRef.Array != array) - { - arrayRef = new EncoderState.ArrayRef(stage, array); - - SignalDirty(DirtyFlags.Textures); - } - } - - public readonly void UpdateImageArray(ShaderStage stage, int binding, ImageArray array) - { - ref EncoderState.ArrayRef arrayRef = ref GetArrayRef(ref _currentState.ImageArrayRefs, binding, ArrayGrowthSize); - - if (arrayRef.Stage != stage || arrayRef.Array != array) - { - arrayRef = new EncoderState.ArrayRef(stage, array); - - SignalDirty(DirtyFlags.Images); - } - } - - public readonly void UpdateImageArraySeparate(ShaderStage stage, int setIndex, ImageArray array) - { - ref EncoderState.ArrayRef arrayRef = ref GetArrayRef(ref _currentState.ImageArrayExtraRefs, setIndex - MetalRenderer.TotalSets); - - if (arrayRef.Stage != stage || arrayRef.Array != array) - { - arrayRef = new EncoderState.ArrayRef(stage, array); - - SignalDirty(DirtyFlags.Images); - } - } - - private static ref EncoderState.ArrayRef GetArrayRef(ref EncoderState.ArrayRef[] array, int index, int growthSize = 1) - { - ArgumentOutOfRangeException.ThrowIfNegative(index); - - if (array.Length <= index) - { - Array.Resize(ref array, index + growthSize); - } - - return ref array[index]; - } - - private readonly void SetDepthStencilState(MTLRenderCommandEncoder renderCommandEncoder) - { - if (DepthStencil != null) - { - MTLDepthStencilState state = _depthStencilCache.GetOrCreate(_currentState.DepthStencilUid); - - renderCommandEncoder.SetDepthStencilState(state); - } - else - { - renderCommandEncoder.SetDepthStencilState(_defaultState); - } - } - - private readonly void SetDepthClamp(MTLRenderCommandEncoder renderCommandEncoder) - { - renderCommandEncoder.SetDepthClipMode(_currentState.DepthClipMode); - } - - private readonly void SetDepthBias(MTLRenderCommandEncoder renderCommandEncoder) - { - renderCommandEncoder.SetDepthBias(_currentState.DepthBias, _currentState.SlopeScale, _currentState.Clamp); - } - - private unsafe void SetScissors(MTLRenderCommandEncoder renderCommandEncoder) - { - bool isTriangles = (_currentState.Topology == PrimitiveTopology.Triangles) || - (_currentState.Topology == PrimitiveTopology.TriangleStrip); - - if (_currentState.CullBoth && isTriangles) - { - renderCommandEncoder.SetScissorRect(new MTLScissorRect { x = 0, y = 0, width = 0, height = 0 }); - } - else - { - if (_currentState.Scissors.Length > 0) - { - fixed (MTLScissorRect* pMtlScissors = _currentState.Scissors) - { - renderCommandEncoder.SetScissorRects((IntPtr)pMtlScissors, (ulong)_currentState.Scissors.Length); - } - } - } - } - - private readonly unsafe void SetViewports(MTLRenderCommandEncoder renderCommandEncoder) - { - if (_currentState.Viewports.Length > 0) - { - fixed (MTLViewport* pMtlViewports = _currentState.Viewports) - { - renderCommandEncoder.SetViewports((IntPtr)pMtlViewports, (ulong)_currentState.Viewports.Length); - } - } - } - - private readonly void UpdatePipelineVertexState(VertexBufferState[] bufferDescriptors, VertexAttribDescriptor[] attribDescriptors) - { - ref PipelineState pipeline = ref _currentState.Pipeline; - uint indexMask = 0; - - for (int i = 0; i < attribDescriptors.Length; i++) - { - ref VertexInputAttributeUid attrib = ref pipeline.Internal.VertexAttributes[i]; - - if (attribDescriptors[i].IsZero) - { - attrib.Format = attribDescriptors[i].Format.Convert(); - indexMask |= 1u << (int)Constants.ZeroBufferIndex; - attrib.BufferIndex = Constants.ZeroBufferIndex; - attrib.Offset = 0; - } - else - { - attrib.Format = attribDescriptors[i].Format.Convert(); - indexMask |= 1u << attribDescriptors[i].BufferIndex; - attrib.BufferIndex = (ulong)attribDescriptors[i].BufferIndex; - attrib.Offset = (ulong)attribDescriptors[i].Offset; - } - } - - for (int i = 0; i < bufferDescriptors.Length; i++) - { - ref VertexInputLayoutUid layout = ref pipeline.Internal.VertexBindings[i]; - - if ((indexMask & (1u << i)) != 0) - { - layout.Stride = (uint)bufferDescriptors[i].Stride; - - if (layout.Stride == 0) - { - layout.Stride = 1; - layout.StepFunction = MTLVertexStepFunction.Constant; - layout.StepRate = 0; - } - else - { - if (bufferDescriptors[i].Divisor > 0) - { - layout.StepFunction = MTLVertexStepFunction.PerInstance; - layout.StepRate = (uint)bufferDescriptors[i].Divisor; - } - else - { - layout.StepFunction = MTLVertexStepFunction.PerVertex; - layout.StepRate = 1; - } - } - } - else - { - layout = new(); - } - } - - ref VertexInputLayoutUid zeroBufLayout = ref pipeline.Internal.VertexBindings[(int)Constants.ZeroBufferIndex]; - - // Zero buffer - if ((indexMask & (1u << (int)Constants.ZeroBufferIndex)) != 0) - { - zeroBufLayout.Stride = 1; - zeroBufLayout.StepFunction = MTLVertexStepFunction.Constant; - zeroBufLayout.StepRate = 0; - } - else - { - zeroBufLayout = new(); - } - - pipeline.VertexAttributeDescriptionsCount = (uint)attribDescriptors.Length; - pipeline.VertexBindingDescriptionsCount = Constants.ZeroBufferIndex + 1; // TODO: move this out? - } - - private readonly void SetVertexBuffers(VertexBufferState[] bufferStates, ref readonly RenderEncoderBindings bindings) - { - for (int i = 0; i < bufferStates.Length; i++) - { - (MTLBuffer mtlBuffer, int offset) = bufferStates[i].GetVertexBuffer(_bufferManager, _pipeline.Cbs); - - if (mtlBuffer.NativePtr != IntPtr.Zero) - { - bindings.VertexBuffers.Add(new BufferResource(mtlBuffer, (ulong)offset, (ulong)i)); - } - } - - Auto autoZeroBuffer = _zeroBuffer == BufferHandle.Null - ? null - : _bufferManager.GetBuffer(_zeroBuffer, false); - - if (autoZeroBuffer == null) - { - return; - } - - MTLBuffer zeroMtlBuffer = autoZeroBuffer.Get(_pipeline.Cbs).Value; - bindings.VertexBuffers.Add(new BufferResource(zeroMtlBuffer, 0, Constants.ZeroBufferIndex)); - } - - private readonly (ulong gpuAddress, IntPtr nativePtr) AddressForBuffer(ref BufferRef buffer) - { - ulong gpuAddress = 0; - IntPtr nativePtr = IntPtr.Zero; - - BufferRange? range = buffer.Range; - Auto autoBuffer = buffer.Buffer; - - if (autoBuffer != null) - { - int offset = 0; - MTLBuffer mtlBuffer; - - if (range.HasValue) - { - offset = range.Value.Offset; - mtlBuffer = autoBuffer.Get(_pipeline.Cbs, offset, range.Value.Size, range.Value.Write).Value; - } - else - { - mtlBuffer = autoBuffer.Get(_pipeline.Cbs).Value; - } - - gpuAddress = mtlBuffer.GpuAddress + (ulong)offset; - nativePtr = mtlBuffer.NativePtr; - } - - return (gpuAddress, nativePtr); - } - - private readonly (ulong gpuAddress, IntPtr nativePtr) AddressForTexture(ref TextureRef texture) - { - TextureBase storage = texture.Storage; - - ulong gpuAddress = 0; - IntPtr nativePtr = IntPtr.Zero; - - if (storage != null) - { - if (storage is TextureBuffer textureBuffer) - { - textureBuffer.RebuildStorage(false); - } - - MTLTexture mtlTexture = storage.GetHandle(); - - gpuAddress = mtlTexture.GpuResourceID._impl; - nativePtr = mtlTexture.NativePtr; - } - - return (gpuAddress, nativePtr); - } - - private readonly (ulong gpuAddress, IntPtr nativePtr) AddressForImage(ref ImageRef image) - { - Texture storage = image.Storage; - - ulong gpuAddress = 0; - IntPtr nativePtr = IntPtr.Zero; - - if (storage != null) - { - MTLTexture mtlTexture = storage.GetHandle(); - - gpuAddress = mtlTexture.GpuResourceID._impl; - nativePtr = mtlTexture.NativePtr; - } - - return (gpuAddress, nativePtr); - } - - private readonly (ulong gpuAddress, IntPtr nativePtr) AddressForTextureBuffer(ref TextureBuffer bufferTexture) - { - ulong gpuAddress = 0; - IntPtr nativePtr = IntPtr.Zero; - - if (bufferTexture != null) - { - bufferTexture.RebuildStorage(false); - - MTLTexture mtlTexture = bufferTexture.GetHandle(); - - gpuAddress = mtlTexture.GpuResourceID._impl; - nativePtr = mtlTexture.NativePtr; - } - - return (gpuAddress, nativePtr); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void AddResource(IntPtr resourcePointer, MTLResourceUsage usage, MTLRenderStages stages, ref readonly RenderEncoderBindings bindings) - { - if (resourcePointer != IntPtr.Zero) - { - bindings.Resources.Add(new Resource(new MTLResource(resourcePointer), usage, stages)); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void AddResource(IntPtr resourcePointer, MTLResourceUsage usage, ref readonly ComputeEncoderBindings bindings) - { - if (resourcePointer != IntPtr.Zero) - { - bindings.Resources.Add(new Resource(new MTLResource(resourcePointer), usage, 0)); - } - } - - private readonly void UpdateAndBind(Program program, uint setIndex, ref readonly RenderEncoderBindings bindings) - { - ResourceBindingSegment[] bindingSegments = program.BindingSegments[setIndex]; - - if (bindingSegments.Length == 0) - { - return; - } - - ScopedTemporaryBuffer vertArgBuffer = default; - ScopedTemporaryBuffer fragArgBuffer = default; - - if (program.ArgumentBufferSizes[setIndex] > 0) - { - vertArgBuffer = _bufferManager.ReserveOrCreate(_pipeline.Cbs, program.ArgumentBufferSizes[setIndex] * sizeof(ulong)); - } - - if (program.FragArgumentBufferSizes[setIndex] > 0) - { - fragArgBuffer = _bufferManager.ReserveOrCreate(_pipeline.Cbs, program.FragArgumentBufferSizes[setIndex] * sizeof(ulong)); - } - - Span vertResourceIds = stackalloc ulong[program.ArgumentBufferSizes[setIndex]]; - Span fragResourceIds = stackalloc ulong[program.FragArgumentBufferSizes[setIndex]]; - - int vertResourceIdIndex = 0; - int fragResourceIdIndex = 0; - - foreach (ResourceBindingSegment segment in bindingSegments) - { - int binding = segment.Binding; - int count = segment.Count; - - switch (setIndex) - { - case Constants.ConstantBuffersSetIndex: - for (int i = 0; i < count; i++) - { - int index = binding + i; - - ref BufferRef buffer = ref _currentState.UniformBufferRefs[index]; - (ulong gpuAddress, IntPtr nativePtr) = AddressForBuffer(ref buffer); - - MTLRenderStages renderStages = 0; - - if ((segment.Stages & ResourceStages.Vertex) != 0) - { - vertResourceIds[vertResourceIdIndex] = gpuAddress; - vertResourceIdIndex++; - - renderStages |= MTLRenderStages.RenderStageVertex; - } - - if ((segment.Stages & ResourceStages.Fragment) != 0) - { - fragResourceIds[fragResourceIdIndex] = gpuAddress; - fragResourceIdIndex++; - - renderStages |= MTLRenderStages.RenderStageFragment; - } - - AddResource(nativePtr, MTLResourceUsage.Read, renderStages, in bindings); - } - break; - case Constants.StorageBuffersSetIndex: - for (int i = 0; i < count; i++) - { - int index = binding + i; - - ref BufferRef buffer = ref _currentState.StorageBufferRefs[index]; - (ulong gpuAddress, IntPtr nativePtr) = AddressForBuffer(ref buffer); - - MTLRenderStages renderStages = 0; - - if ((segment.Stages & ResourceStages.Vertex) != 0) - { - vertResourceIds[vertResourceIdIndex] = gpuAddress; - vertResourceIdIndex++; - - renderStages |= MTLRenderStages.RenderStageVertex; - } - - if ((segment.Stages & ResourceStages.Fragment) != 0) - { - fragResourceIds[fragResourceIdIndex] = gpuAddress; - fragResourceIdIndex++; - - renderStages |= MTLRenderStages.RenderStageFragment; - } - - AddResource(nativePtr, MTLResourceUsage.Read, renderStages, in bindings); - } - break; - case Constants.TexturesSetIndex: - if (!segment.IsArray) - { - for (int i = 0; i < count; i++) - { - int index = binding + i; - - ref TextureRef texture = ref _currentState.TextureRefs[index]; - (ulong gpuAddress, IntPtr nativePtr) = AddressForTexture(ref texture); - - MTLRenderStages renderStages = 0; - - if ((segment.Stages & ResourceStages.Vertex) != 0) - { - vertResourceIds[vertResourceIdIndex] = gpuAddress; - vertResourceIdIndex++; - - if (texture.Sampler != null) - { - vertResourceIds[vertResourceIdIndex] = texture.Sampler.Get(_pipeline.Cbs).Value.GpuResourceID._impl; - vertResourceIdIndex++; - } - - renderStages |= MTLRenderStages.RenderStageVertex; - } - - if ((segment.Stages & ResourceStages.Fragment) != 0) - { - fragResourceIds[fragResourceIdIndex] = gpuAddress; - fragResourceIdIndex++; - - if (texture.Sampler != null) - { - fragResourceIds[fragResourceIdIndex] = texture.Sampler.Get(_pipeline.Cbs).Value.GpuResourceID._impl; - fragResourceIdIndex++; - } - - renderStages |= MTLRenderStages.RenderStageFragment; - } - - AddResource(nativePtr, MTLResourceUsage.Read, renderStages, in bindings); - } - } - else - { - TextureArray textureArray = _currentState.TextureArrayRefs[binding].Array; - - if (segment.Type != ResourceType.BufferTexture) - { - TextureRef[] textures = textureArray.GetTextureRefs(); - Auto[] samplers = new Auto[textures.Length]; - - for (int i = 0; i < textures.Length; i++) - { - TextureRef texture = textures[i]; - (ulong gpuAddress, IntPtr nativePtr) = AddressForTexture(ref texture); - - samplers[i] = texture.Sampler; - - MTLRenderStages renderStages = 0; - - if ((segment.Stages & ResourceStages.Vertex) != 0) - { - vertResourceIds[vertResourceIdIndex] = gpuAddress; - vertResourceIdIndex++; - - renderStages |= MTLRenderStages.RenderStageVertex; - } - - if ((segment.Stages & ResourceStages.Fragment) != 0) - { - fragResourceIds[fragResourceIdIndex] = gpuAddress; - fragResourceIdIndex++; - - renderStages |= MTLRenderStages.RenderStageFragment; - } - - AddResource(nativePtr, MTLResourceUsage.Read, renderStages, in bindings); - } - - foreach (Auto sampler in samplers) - { - ulong gpuAddress = 0; - - if (sampler != null) - { - gpuAddress = sampler.Get(_pipeline.Cbs).Value.GpuResourceID._impl; - } - - if ((segment.Stages & ResourceStages.Vertex) != 0) - { - vertResourceIds[vertResourceIdIndex] = gpuAddress; - vertResourceIdIndex++; - } - - if ((segment.Stages & ResourceStages.Fragment) != 0) - { - fragResourceIds[fragResourceIdIndex] = gpuAddress; - fragResourceIdIndex++; - } - } - } - else - { - TextureBuffer[] bufferTextures = textureArray.GetBufferTextureRefs(); - - for (int i = 0; i < bufferTextures.Length; i++) - { - TextureBuffer bufferTexture = bufferTextures[i]; - (ulong gpuAddress, IntPtr nativePtr) = AddressForTextureBuffer(ref bufferTexture); - - MTLRenderStages renderStages = 0; - - if ((segment.Stages & ResourceStages.Vertex) != 0) - { - vertResourceIds[vertResourceIdIndex] = gpuAddress; - vertResourceIdIndex++; - - renderStages |= MTLRenderStages.RenderStageVertex; - } - - if ((segment.Stages & ResourceStages.Fragment) != 0) - { - fragResourceIds[fragResourceIdIndex] = gpuAddress; - fragResourceIdIndex++; - - renderStages |= MTLRenderStages.RenderStageFragment; - } - - AddResource(nativePtr, MTLResourceUsage.Read, renderStages, in bindings); - } - } - } - break; - case Constants.ImagesSetIndex: - if (!segment.IsArray) - { - for (int i = 0; i < count; i++) - { - int index = binding + i; - - ref ImageRef image = ref _currentState.ImageRefs[index]; - (ulong gpuAddress, IntPtr nativePtr) = AddressForImage(ref image); - - MTLRenderStages renderStages = 0; - - if ((segment.Stages & ResourceStages.Vertex) != 0) - { - vertResourceIds[vertResourceIdIndex] = gpuAddress; - vertResourceIdIndex++; - renderStages |= MTLRenderStages.RenderStageVertex; - } - - if ((segment.Stages & ResourceStages.Fragment) != 0) - { - fragResourceIds[fragResourceIdIndex] = gpuAddress; - fragResourceIdIndex++; - renderStages |= MTLRenderStages.RenderStageFragment; - } - - AddResource(nativePtr, MTLResourceUsage.Read | MTLResourceUsage.Write, renderStages, in bindings); - } - } - else - { - ImageArray imageArray = _currentState.ImageArrayRefs[binding].Array; - - if (segment.Type != ResourceType.BufferImage) - { - TextureRef[] images = imageArray.GetTextureRefs(); - - for (int i = 0; i < images.Length; i++) - { - TextureRef image = images[i]; - (ulong gpuAddress, IntPtr nativePtr) = AddressForTexture(ref image); - - MTLRenderStages renderStages = 0; - - if ((segment.Stages & ResourceStages.Vertex) != 0) - { - vertResourceIds[vertResourceIdIndex] = gpuAddress; - vertResourceIdIndex++; - renderStages |= MTLRenderStages.RenderStageVertex; - } - - if ((segment.Stages & ResourceStages.Fragment) != 0) - { - fragResourceIds[fragResourceIdIndex] = gpuAddress; - fragResourceIdIndex++; - renderStages |= MTLRenderStages.RenderStageFragment; - } - - AddResource(nativePtr, MTLResourceUsage.Read | MTLResourceUsage.Write, renderStages, in bindings); - } - } - else - { - TextureBuffer[] bufferImages = imageArray.GetBufferTextureRefs(); - - for (int i = 0; i < bufferImages.Length; i++) - { - TextureBuffer image = bufferImages[i]; - (ulong gpuAddress, IntPtr nativePtr) = AddressForTextureBuffer(ref image); - - MTLRenderStages renderStages = 0; - - if ((segment.Stages & ResourceStages.Vertex) != 0) - { - vertResourceIds[vertResourceIdIndex] = gpuAddress; - vertResourceIdIndex++; - renderStages |= MTLRenderStages.RenderStageVertex; - } - - if ((segment.Stages & ResourceStages.Fragment) != 0) - { - fragResourceIds[fragResourceIdIndex] = gpuAddress; - fragResourceIdIndex++; - renderStages |= MTLRenderStages.RenderStageFragment; - } - - AddResource(nativePtr, MTLResourceUsage.Read | MTLResourceUsage.Write, renderStages, in bindings); - } - } - } - break; - } - } - - if (program.ArgumentBufferSizes[setIndex] > 0) - { - vertArgBuffer.Holder.SetDataUnchecked(vertArgBuffer.Offset, MemoryMarshal.AsBytes(vertResourceIds)); - MTLBuffer mtlVertArgBuffer = _bufferManager.GetBuffer(vertArgBuffer.Handle, false).Get(_pipeline.Cbs).Value; - bindings.VertexBuffers.Add(new BufferResource(mtlVertArgBuffer, (uint)vertArgBuffer.Range.Offset, SetIndexToBindingIndex(setIndex))); - } - - if (program.FragArgumentBufferSizes[setIndex] > 0) - { - fragArgBuffer.Holder.SetDataUnchecked(fragArgBuffer.Offset, MemoryMarshal.AsBytes(fragResourceIds)); - MTLBuffer mtlFragArgBuffer = _bufferManager.GetBuffer(fragArgBuffer.Handle, false).Get(_pipeline.Cbs).Value; - bindings.FragmentBuffers.Add(new BufferResource(mtlFragArgBuffer, (uint)fragArgBuffer.Range.Offset, SetIndexToBindingIndex(setIndex))); - } - } - - private readonly void UpdateAndBind(Program program, uint setIndex, ref readonly ComputeEncoderBindings bindings) - { - ResourceBindingSegment[] bindingSegments = program.BindingSegments[setIndex]; - - if (bindingSegments.Length == 0) - { - return; - } - - ScopedTemporaryBuffer argBuffer = default; - - if (program.ArgumentBufferSizes[setIndex] > 0) - { - argBuffer = _bufferManager.ReserveOrCreate(_pipeline.Cbs, program.ArgumentBufferSizes[setIndex] * sizeof(ulong)); - } - - Span resourceIds = stackalloc ulong[program.ArgumentBufferSizes[setIndex]]; - int resourceIdIndex = 0; - - foreach (ResourceBindingSegment segment in bindingSegments) - { - int binding = segment.Binding; - int count = segment.Count; - - switch (setIndex) - { - case Constants.ConstantBuffersSetIndex: - for (int i = 0; i < count; i++) - { - int index = binding + i; - - ref BufferRef buffer = ref _currentState.UniformBufferRefs[index]; - (ulong gpuAddress, IntPtr nativePtr) = AddressForBuffer(ref buffer); - - if ((segment.Stages & ResourceStages.Compute) != 0) - { - AddResource(nativePtr, MTLResourceUsage.Read, in bindings); - bindings.Resources.Add(new Resource(new MTLResource(nativePtr), MTLResourceUsage.Read, 0)); - resourceIds[resourceIdIndex] = gpuAddress; - resourceIdIndex++; - } - } - break; - case Constants.StorageBuffersSetIndex: - for (int i = 0; i < count; i++) - { - int index = binding + i; - - ref BufferRef buffer = ref _currentState.StorageBufferRefs[index]; - (ulong gpuAddress, IntPtr nativePtr) = AddressForBuffer(ref buffer); - - if ((segment.Stages & ResourceStages.Compute) != 0) - { - AddResource(nativePtr, MTLResourceUsage.Read | MTLResourceUsage.Write, in bindings); - resourceIds[resourceIdIndex] = gpuAddress; - resourceIdIndex++; - } - } - break; - case Constants.TexturesSetIndex: - if (!segment.IsArray) - { - for (int i = 0; i < count; i++) - { - int index = binding + i; - - ref TextureRef texture = ref _currentState.TextureRefs[index]; - (ulong gpuAddress, IntPtr nativePtr) = AddressForTexture(ref texture); - - if ((segment.Stages & ResourceStages.Compute) != 0) - { - AddResource(nativePtr, MTLResourceUsage.Read, in bindings); - resourceIds[resourceIdIndex] = gpuAddress; - resourceIdIndex++; - - if (texture.Sampler != null) - { - resourceIds[resourceIdIndex] = texture.Sampler.Get(_pipeline.Cbs).Value.GpuResourceID._impl; - resourceIdIndex++; - } - } - } - } - else - { - TextureArray textureArray = _currentState.TextureArrayRefs[binding].Array; - - if (segment.Type != ResourceType.BufferTexture) - { - TextureRef[] textures = textureArray.GetTextureRefs(); - Auto[] samplers = new Auto[textures.Length]; - - for (int i = 0; i < textures.Length; i++) - { - TextureRef texture = textures[i]; - (ulong gpuAddress, IntPtr nativePtr) = AddressForTexture(ref texture); - - if ((segment.Stages & ResourceStages.Compute) != 0) - { - AddResource(nativePtr, MTLResourceUsage.Read, in bindings); - resourceIds[resourceIdIndex] = gpuAddress; - resourceIdIndex++; - - samplers[i] = texture.Sampler; - } - } - - foreach (Auto sampler in samplers) - { - if (sampler != null) - { - resourceIds[resourceIdIndex] = sampler.Get(_pipeline.Cbs).Value.GpuResourceID._impl; - resourceIdIndex++; - } - } - } - else - { - TextureBuffer[] bufferTextures = textureArray.GetBufferTextureRefs(); - - for (int i = 0; i < bufferTextures.Length; i++) - { - TextureBuffer bufferTexture = bufferTextures[i]; - (ulong gpuAddress, IntPtr nativePtr) = AddressForTextureBuffer(ref bufferTexture); - - if ((segment.Stages & ResourceStages.Compute) != 0) - { - AddResource(nativePtr, MTLResourceUsage.Read, in bindings); - resourceIds[resourceIdIndex] = gpuAddress; - resourceIdIndex++; - } - } - } - } - break; - case Constants.ImagesSetIndex: - if (!segment.IsArray) - { - for (int i = 0; i < count; i++) - { - int index = binding + i; - - ref ImageRef image = ref _currentState.ImageRefs[index]; - (ulong gpuAddress, IntPtr nativePtr) = AddressForImage(ref image); - - if ((segment.Stages & ResourceStages.Compute) != 0) - { - AddResource(nativePtr, MTLResourceUsage.Read | MTLResourceUsage.Write, in bindings); - resourceIds[resourceIdIndex] = gpuAddress; - resourceIdIndex++; - } - } - } - else - { - ImageArray imageArray = _currentState.ImageArrayRefs[binding].Array; - - if (segment.Type != ResourceType.BufferImage) - { - TextureRef[] images = imageArray.GetTextureRefs(); - - for (int i = 0; i < images.Length; i++) - { - TextureRef image = images[i]; - (ulong gpuAddress, IntPtr nativePtr) = AddressForTexture(ref image); - - if ((segment.Stages & ResourceStages.Compute) != 0) - { - AddResource(nativePtr, MTLResourceUsage.Read | MTLResourceUsage.Write, in bindings); - resourceIds[resourceIdIndex] = gpuAddress; - resourceIdIndex++; - } - } - } - else - { - TextureBuffer[] bufferImages = imageArray.GetBufferTextureRefs(); - - for (int i = 0; i < bufferImages.Length; i++) - { - TextureBuffer image = bufferImages[i]; - (ulong gpuAddress, IntPtr nativePtr) = AddressForTextureBuffer(ref image); - - if ((segment.Stages & ResourceStages.Compute) != 0) - { - AddResource(nativePtr, MTLResourceUsage.Read | MTLResourceUsage.Write, in bindings); - resourceIds[resourceIdIndex] = gpuAddress; - resourceIdIndex++; - } - } - } - } - break; - } - } - - if (program.ArgumentBufferSizes[setIndex] > 0) - { - argBuffer.Holder.SetDataUnchecked(argBuffer.Offset, MemoryMarshal.AsBytes(resourceIds)); - MTLBuffer mtlArgBuffer = _bufferManager.GetBuffer(argBuffer.Handle, false).Get(_pipeline.Cbs).Value; - bindings.Buffers.Add(new BufferResource(mtlArgBuffer, (uint)argBuffer.Range.Offset, SetIndexToBindingIndex(setIndex))); - } - } - - private static uint SetIndexToBindingIndex(uint setIndex) - { - return setIndex switch - { - Constants.ConstantBuffersSetIndex => Constants.ConstantBuffersIndex, - Constants.StorageBuffersSetIndex => Constants.StorageBuffersIndex, - Constants.TexturesSetIndex => Constants.TexturesIndex, - Constants.ImagesSetIndex => Constants.ImagesIndex, - _ => throw new NotImplementedException() - }; - } - - private readonly void SetCullMode(MTLRenderCommandEncoder renderCommandEncoder) - { - renderCommandEncoder.SetCullMode(_currentState.CullMode); - } - - private readonly void SetFrontFace(MTLRenderCommandEncoder renderCommandEncoder) - { - renderCommandEncoder.SetFrontFacingWinding(_currentState.Winding); - } - - private readonly void SetStencilRefValue(MTLRenderCommandEncoder renderCommandEncoder) - { - renderCommandEncoder.SetStencilReferenceValues((uint)_currentState.FrontRefValue, (uint)_currentState.BackRefValue); - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/EnumConversion.cs b/src/Ryujinx.Graphics.Metal/EnumConversion.cs deleted file mode 100644 index 7cfb5cbd4..000000000 --- a/src/Ryujinx.Graphics.Metal/EnumConversion.cs +++ /dev/null @@ -1,292 +0,0 @@ -using Ryujinx.Common.Logging; -using Ryujinx.Graphics.GAL; -using SharpMetal.Metal; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - static class EnumConversion - { - public static MTLSamplerAddressMode Convert(this AddressMode mode) - { - return mode switch - { - AddressMode.Clamp => MTLSamplerAddressMode.ClampToEdge, // TODO: Should be clamp. - AddressMode.Repeat => MTLSamplerAddressMode.Repeat, - AddressMode.MirrorClamp => MTLSamplerAddressMode.MirrorClampToEdge, // TODO: Should be mirror clamp. - AddressMode.MirroredRepeat => MTLSamplerAddressMode.MirrorRepeat, - AddressMode.ClampToBorder => MTLSamplerAddressMode.ClampToBorderColor, - AddressMode.ClampToEdge => MTLSamplerAddressMode.ClampToEdge, - AddressMode.MirrorClampToEdge => MTLSamplerAddressMode.MirrorClampToEdge, - AddressMode.MirrorClampToBorder => MTLSamplerAddressMode.ClampToBorderColor, // TODO: Should be mirror clamp to border. - _ => LogInvalidAndReturn(mode, nameof(AddressMode), MTLSamplerAddressMode.ClampToEdge) // TODO: Should be clamp. - }; - } - - public static MTLBlendFactor Convert(this BlendFactor factor) - { - return factor switch - { - BlendFactor.Zero or BlendFactor.ZeroGl => MTLBlendFactor.Zero, - BlendFactor.One or BlendFactor.OneGl => MTLBlendFactor.One, - BlendFactor.SrcColor or BlendFactor.SrcColorGl => MTLBlendFactor.SourceColor, - BlendFactor.OneMinusSrcColor or BlendFactor.OneMinusSrcColorGl => MTLBlendFactor.OneMinusSourceColor, - BlendFactor.SrcAlpha or BlendFactor.SrcAlphaGl => MTLBlendFactor.SourceAlpha, - BlendFactor.OneMinusSrcAlpha or BlendFactor.OneMinusSrcAlphaGl => MTLBlendFactor.OneMinusSourceAlpha, - BlendFactor.DstAlpha or BlendFactor.DstAlphaGl => MTLBlendFactor.DestinationAlpha, - BlendFactor.OneMinusDstAlpha or BlendFactor.OneMinusDstAlphaGl => MTLBlendFactor.OneMinusDestinationAlpha, - BlendFactor.DstColor or BlendFactor.DstColorGl => MTLBlendFactor.DestinationColor, - BlendFactor.OneMinusDstColor or BlendFactor.OneMinusDstColorGl => MTLBlendFactor.OneMinusDestinationColor, - BlendFactor.SrcAlphaSaturate or BlendFactor.SrcAlphaSaturateGl => MTLBlendFactor.SourceAlphaSaturated, - BlendFactor.Src1Color or BlendFactor.Src1ColorGl => MTLBlendFactor.Source1Color, - BlendFactor.OneMinusSrc1Color or BlendFactor.OneMinusSrc1ColorGl => MTLBlendFactor.OneMinusSource1Color, - BlendFactor.Src1Alpha or BlendFactor.Src1AlphaGl => MTLBlendFactor.Source1Alpha, - BlendFactor.OneMinusSrc1Alpha or BlendFactor.OneMinusSrc1AlphaGl => MTLBlendFactor.OneMinusSource1Alpha, - BlendFactor.ConstantColor => MTLBlendFactor.BlendColor, - BlendFactor.OneMinusConstantColor => MTLBlendFactor.OneMinusBlendColor, - BlendFactor.ConstantAlpha => MTLBlendFactor.BlendAlpha, - BlendFactor.OneMinusConstantAlpha => MTLBlendFactor.OneMinusBlendAlpha, - _ => LogInvalidAndReturn(factor, nameof(BlendFactor), MTLBlendFactor.Zero) - }; - } - - public static MTLBlendOperation Convert(this BlendOp op) - { - return op switch - { - BlendOp.Add or BlendOp.AddGl => MTLBlendOperation.Add, - BlendOp.Subtract or BlendOp.SubtractGl => MTLBlendOperation.Subtract, - BlendOp.ReverseSubtract or BlendOp.ReverseSubtractGl => MTLBlendOperation.ReverseSubtract, - BlendOp.Minimum => MTLBlendOperation.Min, - BlendOp.Maximum => MTLBlendOperation.Max, - _ => LogInvalidAndReturn(op, nameof(BlendOp), MTLBlendOperation.Add) - }; - } - - public static MTLCompareFunction Convert(this CompareOp op) - { - return op switch - { - CompareOp.Never or CompareOp.NeverGl => MTLCompareFunction.Never, - CompareOp.Less or CompareOp.LessGl => MTLCompareFunction.Less, - CompareOp.Equal or CompareOp.EqualGl => MTLCompareFunction.Equal, - CompareOp.LessOrEqual or CompareOp.LessOrEqualGl => MTLCompareFunction.LessEqual, - CompareOp.Greater or CompareOp.GreaterGl => MTLCompareFunction.Greater, - CompareOp.NotEqual or CompareOp.NotEqualGl => MTLCompareFunction.NotEqual, - CompareOp.GreaterOrEqual or CompareOp.GreaterOrEqualGl => MTLCompareFunction.GreaterEqual, - CompareOp.Always or CompareOp.AlwaysGl => MTLCompareFunction.Always, - _ => LogInvalidAndReturn(op, nameof(CompareOp), MTLCompareFunction.Never) - }; - } - - public static MTLCullMode Convert(this Face face) - { - return face switch - { - Face.Back => MTLCullMode.Back, - Face.Front => MTLCullMode.Front, - Face.FrontAndBack => MTLCullMode.None, - _ => LogInvalidAndReturn(face, nameof(Face), MTLCullMode.Back) - }; - } - - public static MTLWinding Convert(this FrontFace frontFace) - { - // The viewport is flipped vertically, therefore we need to switch the winding order as well - return frontFace switch - { - FrontFace.Clockwise => MTLWinding.CounterClockwise, - FrontFace.CounterClockwise => MTLWinding.Clockwise, - _ => LogInvalidAndReturn(frontFace, nameof(FrontFace), MTLWinding.Clockwise) - }; - } - - public static MTLIndexType Convert(this IndexType type) - { - return type switch - { - IndexType.UShort => MTLIndexType.UInt16, - IndexType.UInt => MTLIndexType.UInt32, - _ => LogInvalidAndReturn(type, nameof(IndexType), MTLIndexType.UInt16) - }; - } - - public static MTLLogicOperation Convert(this LogicalOp op) - { - return op switch - { - LogicalOp.Clear => MTLLogicOperation.Clear, - LogicalOp.And => MTLLogicOperation.And, - LogicalOp.AndReverse => MTLLogicOperation.AndReverse, - LogicalOp.Copy => MTLLogicOperation.Copy, - LogicalOp.AndInverted => MTLLogicOperation.AndInverted, - LogicalOp.Noop => MTLLogicOperation.Noop, - LogicalOp.Xor => MTLLogicOperation.Xor, - LogicalOp.Or => MTLLogicOperation.Or, - LogicalOp.Nor => MTLLogicOperation.Nor, - LogicalOp.Equiv => MTLLogicOperation.Equivalence, - LogicalOp.Invert => MTLLogicOperation.Invert, - LogicalOp.OrReverse => MTLLogicOperation.OrReverse, - LogicalOp.CopyInverted => MTLLogicOperation.CopyInverted, - LogicalOp.OrInverted => MTLLogicOperation.OrInverted, - LogicalOp.Nand => MTLLogicOperation.Nand, - LogicalOp.Set => MTLLogicOperation.Set, - _ => LogInvalidAndReturn(op, nameof(LogicalOp), MTLLogicOperation.And) - }; - } - - public static MTLSamplerMinMagFilter Convert(this MagFilter filter) - { - return filter switch - { - MagFilter.Nearest => MTLSamplerMinMagFilter.Nearest, - MagFilter.Linear => MTLSamplerMinMagFilter.Linear, - _ => LogInvalidAndReturn(filter, nameof(MagFilter), MTLSamplerMinMagFilter.Nearest) - }; - } - - public static (MTLSamplerMinMagFilter, MTLSamplerMipFilter) Convert(this MinFilter filter) - { - return filter switch - { - MinFilter.Nearest => (MTLSamplerMinMagFilter.Nearest, MTLSamplerMipFilter.Nearest), - MinFilter.Linear => (MTLSamplerMinMagFilter.Linear, MTLSamplerMipFilter.Linear), - MinFilter.NearestMipmapNearest => (MTLSamplerMinMagFilter.Nearest, MTLSamplerMipFilter.Nearest), - MinFilter.LinearMipmapNearest => (MTLSamplerMinMagFilter.Linear, MTLSamplerMipFilter.Nearest), - MinFilter.NearestMipmapLinear => (MTLSamplerMinMagFilter.Nearest, MTLSamplerMipFilter.Linear), - MinFilter.LinearMipmapLinear => (MTLSamplerMinMagFilter.Linear, MTLSamplerMipFilter.Linear), - _ => LogInvalidAndReturn(filter, nameof(MinFilter), (MTLSamplerMinMagFilter.Nearest, MTLSamplerMipFilter.Nearest)) - - }; - } - - public static MTLPrimitiveType Convert(this PrimitiveTopology topology) - { - return topology switch - { - PrimitiveTopology.Points => MTLPrimitiveType.Point, - PrimitiveTopology.Lines => MTLPrimitiveType.Line, - PrimitiveTopology.LineStrip => MTLPrimitiveType.LineStrip, - PrimitiveTopology.Triangles => MTLPrimitiveType.Triangle, - PrimitiveTopology.TriangleStrip => MTLPrimitiveType.TriangleStrip, - _ => LogInvalidAndReturn(topology, nameof(PrimitiveTopology), MTLPrimitiveType.Triangle) - }; - } - - public static MTLStencilOperation Convert(this StencilOp op) - { - return op switch - { - StencilOp.Keep or StencilOp.KeepGl => MTLStencilOperation.Keep, - StencilOp.Zero or StencilOp.ZeroGl => MTLStencilOperation.Zero, - StencilOp.Replace or StencilOp.ReplaceGl => MTLStencilOperation.Replace, - StencilOp.IncrementAndClamp or StencilOp.IncrementAndClampGl => MTLStencilOperation.IncrementClamp, - StencilOp.DecrementAndClamp or StencilOp.DecrementAndClampGl => MTLStencilOperation.DecrementClamp, - StencilOp.Invert or StencilOp.InvertGl => MTLStencilOperation.Invert, - StencilOp.IncrementAndWrap or StencilOp.IncrementAndWrapGl => MTLStencilOperation.IncrementWrap, - StencilOp.DecrementAndWrap or StencilOp.DecrementAndWrapGl => MTLStencilOperation.DecrementWrap, - _ => LogInvalidAndReturn(op, nameof(StencilOp), MTLStencilOperation.Keep) - }; - } - - public static MTLTextureType Convert(this Target target) - { - return target switch - { - Target.TextureBuffer => MTLTextureType.TextureBuffer, - Target.Texture1D => MTLTextureType.Type1D, - Target.Texture1DArray => MTLTextureType.Type1DArray, - Target.Texture2D => MTLTextureType.Type2D, - Target.Texture2DArray => MTLTextureType.Type2DArray, - Target.Texture2DMultisample => MTLTextureType.Type2DMultisample, - Target.Texture2DMultisampleArray => MTLTextureType.Type2DMultisampleArray, - Target.Texture3D => MTLTextureType.Type3D, - Target.Cubemap => MTLTextureType.Cube, - Target.CubemapArray => MTLTextureType.CubeArray, - _ => LogInvalidAndReturn(target, nameof(Target), MTLTextureType.Type2D) - }; - } - - public static MTLTextureSwizzle Convert(this SwizzleComponent swizzleComponent) - { - return swizzleComponent switch - { - SwizzleComponent.Zero => MTLTextureSwizzle.Zero, - SwizzleComponent.One => MTLTextureSwizzle.One, - SwizzleComponent.Red => MTLTextureSwizzle.Red, - SwizzleComponent.Green => MTLTextureSwizzle.Green, - SwizzleComponent.Blue => MTLTextureSwizzle.Blue, - SwizzleComponent.Alpha => MTLTextureSwizzle.Alpha, - _ => LogInvalidAndReturn(swizzleComponent, nameof(SwizzleComponent), MTLTextureSwizzle.Zero) - }; - } - - public static MTLVertexFormat Convert(this Format format) - { - return format switch - { - Format.R16Float => MTLVertexFormat.Half, - Format.R16G16Float => MTLVertexFormat.Half2, - Format.R16G16B16Float => MTLVertexFormat.Half3, - Format.R16G16B16A16Float => MTLVertexFormat.Half4, - Format.R32Float => MTLVertexFormat.Float, - Format.R32G32Float => MTLVertexFormat.Float2, - Format.R32G32B32Float => MTLVertexFormat.Float3, - Format.R11G11B10Float => MTLVertexFormat.FloatRG11B10, - Format.R32G32B32A32Float => MTLVertexFormat.Float4, - Format.R8Uint => MTLVertexFormat.UChar, - Format.R8G8Uint => MTLVertexFormat.UChar2, - Format.R8G8B8Uint => MTLVertexFormat.UChar3, - Format.R8G8B8A8Uint => MTLVertexFormat.UChar4, - Format.R16Uint => MTLVertexFormat.UShort, - Format.R16G16Uint => MTLVertexFormat.UShort2, - Format.R16G16B16Uint => MTLVertexFormat.UShort3, - Format.R16G16B16A16Uint => MTLVertexFormat.UShort4, - Format.R32Uint => MTLVertexFormat.UInt, - Format.R32G32Uint => MTLVertexFormat.UInt2, - Format.R32G32B32Uint => MTLVertexFormat.UInt3, - Format.R32G32B32A32Uint => MTLVertexFormat.UInt4, - Format.R8Sint => MTLVertexFormat.Char, - Format.R8G8Sint => MTLVertexFormat.Char2, - Format.R8G8B8Sint => MTLVertexFormat.Char3, - Format.R8G8B8A8Sint => MTLVertexFormat.Char4, - Format.R16Sint => MTLVertexFormat.Short, - Format.R16G16Sint => MTLVertexFormat.Short2, - Format.R16G16B16Sint => MTLVertexFormat.Short3, - Format.R16G16B16A16Sint => MTLVertexFormat.Short4, - Format.R32Sint => MTLVertexFormat.Int, - Format.R32G32Sint => MTLVertexFormat.Int2, - Format.R32G32B32Sint => MTLVertexFormat.Int3, - Format.R32G32B32A32Sint => MTLVertexFormat.Int4, - Format.R8Unorm => MTLVertexFormat.UCharNormalized, - Format.R8G8Unorm => MTLVertexFormat.UChar2Normalized, - Format.R8G8B8Unorm => MTLVertexFormat.UChar3Normalized, - Format.R8G8B8A8Unorm => MTLVertexFormat.UChar4Normalized, - Format.R16Unorm => MTLVertexFormat.UShortNormalized, - Format.R16G16Unorm => MTLVertexFormat.UShort2Normalized, - Format.R16G16B16Unorm => MTLVertexFormat.UShort3Normalized, - Format.R16G16B16A16Unorm => MTLVertexFormat.UShort4Normalized, - Format.R10G10B10A2Unorm => MTLVertexFormat.UInt1010102Normalized, - Format.R8Snorm => MTLVertexFormat.CharNormalized, - Format.R8G8Snorm => MTLVertexFormat.Char2Normalized, - Format.R8G8B8Snorm => MTLVertexFormat.Char3Normalized, - Format.R8G8B8A8Snorm => MTLVertexFormat.Char4Normalized, - Format.R16Snorm => MTLVertexFormat.ShortNormalized, - Format.R16G16Snorm => MTLVertexFormat.Short2Normalized, - Format.R16G16B16Snorm => MTLVertexFormat.Short3Normalized, - Format.R16G16B16A16Snorm => MTLVertexFormat.Short4Normalized, - Format.R10G10B10A2Snorm => MTLVertexFormat.Int1010102Normalized, - - _ => LogInvalidAndReturn(format, nameof(Format), MTLVertexFormat.Float4) - }; - } - - private static T2 LogInvalidAndReturn(T1 value, string name, T2 defaultValue = default) - { - Logger.Debug?.Print(LogClass.Gpu, $"Invalid {name} enum value: {value}."); - - return defaultValue; - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/FenceHolder.cs b/src/Ryujinx.Graphics.Metal/FenceHolder.cs deleted file mode 100644 index a8dd28c0d..000000000 --- a/src/Ryujinx.Graphics.Metal/FenceHolder.cs +++ /dev/null @@ -1,77 +0,0 @@ -using SharpMetal.Metal; -using System; -using System.Runtime.Versioning; -using System.Threading; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - class FenceHolder : IDisposable - { - private MTLCommandBuffer _fence; - private int _referenceCount; - private bool _disposed; - - public FenceHolder(MTLCommandBuffer fence) - { - _fence = fence; - _referenceCount = 1; - } - - public MTLCommandBuffer GetUnsafe() - { - return _fence; - } - - public bool TryGet(out MTLCommandBuffer fence) - { - int lastValue; - do - { - lastValue = _referenceCount; - - if (lastValue == 0) - { - fence = default; - return false; - } - } while (Interlocked.CompareExchange(ref _referenceCount, lastValue + 1, lastValue) != lastValue); - - fence = _fence; - return true; - } - - public MTLCommandBuffer Get() - { - Interlocked.Increment(ref _referenceCount); - return _fence; - } - - public void Put() - { - if (Interlocked.Decrement(ref _referenceCount) == 0) - { - _fence = default; - } - } - - public void Wait() - { - _fence.WaitUntilCompleted(); - } - - public bool IsSignaled() - { - return _fence.Status == MTLCommandBufferStatus.Completed; - } - - public void Dispose() - { - if (!_disposed) - { - Put(); - _disposed = true; - } - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/FormatConverter.cs b/src/Ryujinx.Graphics.Metal/FormatConverter.cs deleted file mode 100644 index e099187b8..000000000 --- a/src/Ryujinx.Graphics.Metal/FormatConverter.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -namespace Ryujinx.Graphics.Metal -{ - class FormatConverter - { - public static void ConvertD24S8ToD32FS8(Span output, ReadOnlySpan input) - { - const float UnormToFloat = 1f / 0xffffff; - - Span outputUint = MemoryMarshal.Cast(output); - ReadOnlySpan inputUint = MemoryMarshal.Cast(input); - - int i = 0; - - for (; i < inputUint.Length; i++) - { - uint depthStencil = inputUint[i]; - uint depth = depthStencil >> 8; - uint stencil = depthStencil & 0xff; - - int j = i * 2; - - outputUint[j] = (uint)BitConverter.SingleToInt32Bits(depth * UnormToFloat); - outputUint[j + 1] = stencil; - } - } - - public static void ConvertD32FS8ToD24S8(Span output, ReadOnlySpan input) - { - Span outputUint = MemoryMarshal.Cast(output); - ReadOnlySpan inputUint = MemoryMarshal.Cast(input); - - int i = 0; - - for (; i < inputUint.Length; i += 2) - { - float depth = BitConverter.Int32BitsToSingle((int)inputUint[i]); - uint stencil = inputUint[i + 1]; - uint depthStencil = (Math.Clamp((uint)(depth * 0xffffff), 0, 0xffffff) << 8) | (stencil & 0xff); - - int j = i >> 1; - - outputUint[j] = depthStencil; - } - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/FormatTable.cs b/src/Ryujinx.Graphics.Metal/FormatTable.cs deleted file mode 100644 index 10c8b435c..000000000 --- a/src/Ryujinx.Graphics.Metal/FormatTable.cs +++ /dev/null @@ -1,196 +0,0 @@ -using Ryujinx.Common.Logging; -using Ryujinx.Graphics.GAL; -using SharpMetal.Metal; -using System; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - static class FormatTable - { - private static readonly MTLPixelFormat[] _table; - - static FormatTable() - { - _table = new MTLPixelFormat[Enum.GetNames(typeof(Format)).Length]; - - Add(Format.R8Unorm, MTLPixelFormat.R8Unorm); - Add(Format.R8Snorm, MTLPixelFormat.R8Snorm); - Add(Format.R8Uint, MTLPixelFormat.R8Uint); - Add(Format.R8Sint, MTLPixelFormat.R8Sint); - Add(Format.R16Float, MTLPixelFormat.R16Float); - Add(Format.R16Unorm, MTLPixelFormat.R16Unorm); - Add(Format.R16Snorm, MTLPixelFormat.R16Snorm); - Add(Format.R16Uint, MTLPixelFormat.R16Uint); - Add(Format.R16Sint, MTLPixelFormat.R16Sint); - Add(Format.R32Float, MTLPixelFormat.R32Float); - Add(Format.R32Uint, MTLPixelFormat.R32Uint); - Add(Format.R32Sint, MTLPixelFormat.R32Sint); - Add(Format.R8G8Unorm, MTLPixelFormat.RG8Unorm); - Add(Format.R8G8Snorm, MTLPixelFormat.RG8Snorm); - Add(Format.R8G8Uint, MTLPixelFormat.RG8Uint); - Add(Format.R8G8Sint, MTLPixelFormat.RG8Sint); - Add(Format.R16G16Float, MTLPixelFormat.RG16Float); - Add(Format.R16G16Unorm, MTLPixelFormat.RG16Unorm); - Add(Format.R16G16Snorm, MTLPixelFormat.RG16Snorm); - Add(Format.R16G16Uint, MTLPixelFormat.RG16Uint); - Add(Format.R16G16Sint, MTLPixelFormat.RG16Sint); - Add(Format.R32G32Float, MTLPixelFormat.RG32Float); - Add(Format.R32G32Uint, MTLPixelFormat.RG32Uint); - Add(Format.R32G32Sint, MTLPixelFormat.RG32Sint); - // Add(Format.R8G8B8Unorm, MTLPixelFormat.R8G8B8Unorm); - // Add(Format.R8G8B8Snorm, MTLPixelFormat.R8G8B8Snorm); - // Add(Format.R8G8B8Uint, MTLPixelFormat.R8G8B8Uint); - // Add(Format.R8G8B8Sint, MTLPixelFormat.R8G8B8Sint); - // Add(Format.R16G16B16Float, MTLPixelFormat.R16G16B16Float); - // Add(Format.R16G16B16Unorm, MTLPixelFormat.R16G16B16Unorm); - // Add(Format.R16G16B16Snorm, MTLPixelFormat.R16G16B16SNorm); - // Add(Format.R16G16B16Uint, MTLPixelFormat.R16G16B16Uint); - // Add(Format.R16G16B16Sint, MTLPixelFormat.R16G16B16Sint); - // Add(Format.R32G32B32Float, MTLPixelFormat.R32G32B32Sfloat); - // Add(Format.R32G32B32Uint, MTLPixelFormat.R32G32B32Uint); - // Add(Format.R32G32B32Sint, MTLPixelFormat.R32G32B32Sint); - Add(Format.R8G8B8A8Unorm, MTLPixelFormat.RGBA8Unorm); - Add(Format.R8G8B8A8Snorm, MTLPixelFormat.RGBA8Snorm); - Add(Format.R8G8B8A8Uint, MTLPixelFormat.RGBA8Uint); - Add(Format.R8G8B8A8Sint, MTLPixelFormat.RGBA8Sint); - Add(Format.R16G16B16A16Float, MTLPixelFormat.RGBA16Float); - Add(Format.R16G16B16A16Unorm, MTLPixelFormat.RGBA16Unorm); - Add(Format.R16G16B16A16Snorm, MTLPixelFormat.RGBA16Snorm); - Add(Format.R16G16B16A16Uint, MTLPixelFormat.RGBA16Uint); - Add(Format.R16G16B16A16Sint, MTLPixelFormat.RGBA16Sint); - Add(Format.R32G32B32A32Float, MTLPixelFormat.RGBA32Float); - Add(Format.R32G32B32A32Uint, MTLPixelFormat.RGBA32Uint); - Add(Format.R32G32B32A32Sint, MTLPixelFormat.RGBA32Sint); - Add(Format.S8Uint, MTLPixelFormat.Stencil8); - Add(Format.D16Unorm, MTLPixelFormat.Depth16Unorm); - Add(Format.S8UintD24Unorm, MTLPixelFormat.Depth24UnormStencil8); - Add(Format.X8UintD24Unorm, MTLPixelFormat.Depth24UnormStencil8); - Add(Format.D32Float, MTLPixelFormat.Depth32Float); - Add(Format.D24UnormS8Uint, MTLPixelFormat.Depth24UnormStencil8); - Add(Format.D32FloatS8Uint, MTLPixelFormat.Depth32FloatStencil8); - Add(Format.R8G8B8A8Srgb, MTLPixelFormat.RGBA8UnormsRGB); - // Add(Format.R4G4Unorm, MTLPixelFormat.R4G4Unorm); - Add(Format.R4G4B4A4Unorm, MTLPixelFormat.RGBA8Unorm); - // Add(Format.R5G5B5X1Unorm, MTLPixelFormat.R5G5B5X1Unorm); - Add(Format.R5G5B5A1Unorm, MTLPixelFormat.BGR5A1Unorm); - Add(Format.R5G6B5Unorm, MTLPixelFormat.B5G6R5Unorm); - Add(Format.R10G10B10A2Unorm, MTLPixelFormat.RGB10A2Unorm); - Add(Format.R10G10B10A2Uint, MTLPixelFormat.RGB10A2Uint); - Add(Format.R11G11B10Float, MTLPixelFormat.RG11B10Float); - Add(Format.R9G9B9E5Float, MTLPixelFormat.RGB9E5Float); - Add(Format.Bc1RgbaUnorm, MTLPixelFormat.BC1RGBA); - Add(Format.Bc2Unorm, MTLPixelFormat.BC2RGBA); - Add(Format.Bc3Unorm, MTLPixelFormat.BC3RGBA); - Add(Format.Bc1RgbaSrgb, MTLPixelFormat.BC1RGBAsRGB); - Add(Format.Bc2Srgb, MTLPixelFormat.BC2RGBAsRGB); - Add(Format.Bc3Srgb, MTLPixelFormat.BC3RGBAsRGB); - Add(Format.Bc4Unorm, MTLPixelFormat.BC4RUnorm); - Add(Format.Bc4Snorm, MTLPixelFormat.BC4RSnorm); - Add(Format.Bc5Unorm, MTLPixelFormat.BC5RGUnorm); - Add(Format.Bc5Snorm, MTLPixelFormat.BC5RGSnorm); - Add(Format.Bc7Unorm, MTLPixelFormat.BC7RGBAUnorm); - Add(Format.Bc7Srgb, MTLPixelFormat.BC7RGBAUnormsRGB); - Add(Format.Bc6HSfloat, MTLPixelFormat.BC6HRGBFloat); - Add(Format.Bc6HUfloat, MTLPixelFormat.BC6HRGBUfloat); - Add(Format.Etc2RgbUnorm, MTLPixelFormat.ETC2RGB8); - // Add(Format.Etc2RgbaUnorm, MTLPixelFormat.ETC2RGBA8); - Add(Format.Etc2RgbPtaUnorm, MTLPixelFormat.ETC2RGB8A1); - Add(Format.Etc2RgbSrgb, MTLPixelFormat.ETC2RGB8sRGB); - // Add(Format.Etc2RgbaSrgb, MTLPixelFormat.ETC2RGBA8sRGB); - Add(Format.Etc2RgbPtaSrgb, MTLPixelFormat.ETC2RGB8A1sRGB); - // Add(Format.R8Uscaled, MTLPixelFormat.R8Uscaled); - // Add(Format.R8Sscaled, MTLPixelFormat.R8Sscaled); - // Add(Format.R16Uscaled, MTLPixelFormat.R16Uscaled); - // Add(Format.R16Sscaled, MTLPixelFormat.R16Sscaled); - // Add(Format.R32Uscaled, MTLPixelFormat.R32Uscaled); - // Add(Format.R32Sscaled, MTLPixelFormat.R32Sscaled); - // Add(Format.R8G8Uscaled, MTLPixelFormat.R8G8Uscaled); - // Add(Format.R8G8Sscaled, MTLPixelFormat.R8G8Sscaled); - // Add(Format.R16G16Uscaled, MTLPixelFormat.R16G16Uscaled); - // Add(Format.R16G16Sscaled, MTLPixelFormat.R16G16Sscaled); - // Add(Format.R32G32Uscaled, MTLPixelFormat.R32G32Uscaled); - // Add(Format.R32G32Sscaled, MTLPixelFormat.R32G32Sscaled); - // Add(Format.R8G8B8Uscaled, MTLPixelFormat.R8G8B8Uscaled); - // Add(Format.R8G8B8Sscaled, MTLPixelFormat.R8G8B8Sscaled); - // Add(Format.R16G16B16Uscaled, MTLPixelFormat.R16G16B16Uscaled); - // Add(Format.R16G16B16Sscaled, MTLPixelFormat.R16G16B16Sscaled); - // Add(Format.R32G32B32Uscaled, MTLPixelFormat.R32G32B32Uscaled); - // Add(Format.R32G32B32Sscaled, MTLPixelFormat.R32G32B32Sscaled); - // Add(Format.R8G8B8A8Uscaled, MTLPixelFormat.R8G8B8A8Uscaled); - // Add(Format.R8G8B8A8Sscaled, MTLPixelFormat.R8G8B8A8Sscaled); - // Add(Format.R16G16B16A16Uscaled, MTLPixelFormat.R16G16B16A16Uscaled); - // Add(Format.R16G16B16A16Sscaled, MTLPixelFormat.R16G16B16A16Sscaled); - // Add(Format.R32G32B32A32Uscaled, MTLPixelFormat.R32G32B32A32Uscaled); - // Add(Format.R32G32B32A32Sscaled, MTLPixelFormat.R32G32B32A32Sscaled); - // Add(Format.R10G10B10A2Snorm, MTLPixelFormat.A2B10G10R10SNormPack32); - // Add(Format.R10G10B10A2Sint, MTLPixelFormat.A2B10G10R10SintPack32); - // Add(Format.R10G10B10A2Uscaled, MTLPixelFormat.A2B10G10R10UscaledPack32); - // Add(Format.R10G10B10A2Sscaled, MTLPixelFormat.A2B10G10R10SscaledPack32); - Add(Format.Astc4x4Unorm, MTLPixelFormat.ASTC4x4LDR); - Add(Format.Astc5x4Unorm, MTLPixelFormat.ASTC5x4LDR); - Add(Format.Astc5x5Unorm, MTLPixelFormat.ASTC5x5LDR); - Add(Format.Astc6x5Unorm, MTLPixelFormat.ASTC6x5LDR); - Add(Format.Astc6x6Unorm, MTLPixelFormat.ASTC6x6LDR); - Add(Format.Astc8x5Unorm, MTLPixelFormat.ASTC8x5LDR); - Add(Format.Astc8x6Unorm, MTLPixelFormat.ASTC8x6LDR); - Add(Format.Astc8x8Unorm, MTLPixelFormat.ASTC8x8LDR); - Add(Format.Astc10x5Unorm, MTLPixelFormat.ASTC10x5LDR); - Add(Format.Astc10x6Unorm, MTLPixelFormat.ASTC10x6LDR); - Add(Format.Astc10x8Unorm, MTLPixelFormat.ASTC10x8LDR); - Add(Format.Astc10x10Unorm, MTLPixelFormat.ASTC10x10LDR); - Add(Format.Astc12x10Unorm, MTLPixelFormat.ASTC12x10LDR); - Add(Format.Astc12x12Unorm, MTLPixelFormat.ASTC12x12LDR); - Add(Format.Astc4x4Srgb, MTLPixelFormat.ASTC4x4sRGB); - Add(Format.Astc5x4Srgb, MTLPixelFormat.ASTC5x4sRGB); - Add(Format.Astc5x5Srgb, MTLPixelFormat.ASTC5x5sRGB); - Add(Format.Astc6x5Srgb, MTLPixelFormat.ASTC6x5sRGB); - Add(Format.Astc6x6Srgb, MTLPixelFormat.ASTC6x6sRGB); - Add(Format.Astc8x5Srgb, MTLPixelFormat.ASTC8x5sRGB); - Add(Format.Astc8x6Srgb, MTLPixelFormat.ASTC8x6sRGB); - Add(Format.Astc8x8Srgb, MTLPixelFormat.ASTC8x8sRGB); - Add(Format.Astc10x5Srgb, MTLPixelFormat.ASTC10x5sRGB); - Add(Format.Astc10x6Srgb, MTLPixelFormat.ASTC10x6sRGB); - Add(Format.Astc10x8Srgb, MTLPixelFormat.ASTC10x8sRGB); - Add(Format.Astc10x10Srgb, MTLPixelFormat.ASTC10x10sRGB); - Add(Format.Astc12x10Srgb, MTLPixelFormat.ASTC12x10sRGB); - Add(Format.Astc12x12Srgb, MTLPixelFormat.ASTC12x12sRGB); - Add(Format.B5G6R5Unorm, MTLPixelFormat.B5G6R5Unorm); - Add(Format.B5G5R5A1Unorm, MTLPixelFormat.BGR5A1Unorm); - Add(Format.A1B5G5R5Unorm, MTLPixelFormat.A1BGR5Unorm); - Add(Format.B8G8R8A8Unorm, MTLPixelFormat.BGRA8Unorm); - Add(Format.B8G8R8A8Srgb, MTLPixelFormat.BGRA8UnormsRGB); - } - - private static void Add(Format format, MTLPixelFormat mtlFormat) - { - _table[(int)format] = mtlFormat; - } - - public static MTLPixelFormat GetFormat(Format format) - { - MTLPixelFormat mtlFormat = _table[(int)format]; - - if (IsD24S8(format)) - { - if (!MTLDevice.CreateSystemDefaultDevice().Depth24Stencil8PixelFormatSupported) - { - mtlFormat = MTLPixelFormat.Depth32FloatStencil8; - } - } - - if (mtlFormat == MTLPixelFormat.Invalid) - { - Logger.Error?.PrintMsg(LogClass.Gpu, $"Format {format} is not supported by the host."); - } - - return mtlFormat; - } - - public static bool IsD24S8(Format format) - { - return format == Format.D24UnormS8Uint || format == Format.S8UintD24Unorm || format == Format.X8UintD24Unorm; - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/HardwareInfo.cs b/src/Ryujinx.Graphics.Metal/HardwareInfo.cs deleted file mode 100644 index f6a132f09..000000000 --- a/src/Ryujinx.Graphics.Metal/HardwareInfo.cs +++ /dev/null @@ -1,82 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -namespace Ryujinx.Graphics.Metal -{ - static partial class HardwareInfoTools - { - - private readonly static IntPtr _kCFAllocatorDefault = IntPtr.Zero; - private readonly static UInt32 _kCFStringEncodingASCII = 0x0600; - private const string IOKit = "/System/Library/Frameworks/IOKit.framework/IOKit"; - private const string CoreFoundation = "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation"; - - [LibraryImport(IOKit, StringMarshalling = StringMarshalling.Utf8)] - private static partial IntPtr IOServiceMatching(string name); - - [LibraryImport(IOKit)] - private static partial IntPtr IOServiceGetMatchingService(IntPtr mainPort, IntPtr matching); - - [LibraryImport(IOKit)] - private static partial IntPtr IORegistryEntryCreateCFProperty(IntPtr entry, IntPtr key, IntPtr allocator, UInt32 options); - - [LibraryImport(CoreFoundation, StringMarshalling = StringMarshalling.Utf8)] - private static partial IntPtr CFStringCreateWithCString(IntPtr allocator, string cString, UInt32 encoding); - - [LibraryImport(CoreFoundation)] - [return: MarshalAs(UnmanagedType.U1)] - public static partial bool CFStringGetCString(IntPtr theString, IntPtr buffer, long bufferSizes, UInt32 encoding); - - [LibraryImport(CoreFoundation)] - public static partial IntPtr CFDataGetBytePtr(IntPtr theData); - - static string GetNameFromId(uint id) - { - return id switch - { - 0x1002 => "AMD", - 0x106B => "Apple", - 0x10DE => "NVIDIA", - 0x13B5 => "ARM", - 0x8086 => "Intel", - _ => $"0x{id:X}" - }; - } - - public static string GetVendor() - { - IntPtr serviceDict = IOServiceMatching("IOGPU"); - IntPtr service = IOServiceGetMatchingService(IntPtr.Zero, serviceDict); - IntPtr cfString = CFStringCreateWithCString(_kCFAllocatorDefault, "vendor-id", _kCFStringEncodingASCII); - IntPtr cfProperty = IORegistryEntryCreateCFProperty(service, cfString, _kCFAllocatorDefault, 0); - - byte[] buffer = new byte[4]; - IntPtr bufferPtr = CFDataGetBytePtr(cfProperty); - Marshal.Copy(bufferPtr, buffer, 0, buffer.Length); - - uint vendorId = BitConverter.ToUInt32(buffer); - - return GetNameFromId(vendorId); - } - - public static string GetModel() - { - IntPtr serviceDict = IOServiceMatching("IOGPU"); - IntPtr service = IOServiceGetMatchingService(IntPtr.Zero, serviceDict); - IntPtr cfString = CFStringCreateWithCString(_kCFAllocatorDefault, "model", _kCFStringEncodingASCII); - IntPtr cfProperty = IORegistryEntryCreateCFProperty(service, cfString, _kCFAllocatorDefault, 0); - - char[] buffer = new char[64]; - IntPtr bufferPtr = Marshal.AllocHGlobal(buffer.Length); - - if (CFStringGetCString(cfProperty, bufferPtr, buffer.Length, _kCFStringEncodingASCII)) - { - string model = Marshal.PtrToStringUTF8(bufferPtr); - Marshal.FreeHGlobal(bufferPtr); - return model; - } - - return string.Empty; - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/HashTableSlim.cs b/src/Ryujinx.Graphics.Metal/HashTableSlim.cs deleted file mode 100644 index 267acc6f4..000000000 --- a/src/Ryujinx.Graphics.Metal/HashTableSlim.cs +++ /dev/null @@ -1,143 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Runtime.CompilerServices; - -namespace Ryujinx.Graphics.Metal -{ - interface IRefEquatable - { - bool Equals(ref T other); - } - - class HashTableSlim where TKey : IRefEquatable - { - private const int TotalBuckets = 16; // Must be power of 2 - private const int TotalBucketsMask = TotalBuckets - 1; - - private struct Entry - { - public int Hash; - public TKey Key; - public TValue Value; - } - - private struct Bucket - { - public int Length; - public Entry[] Entries; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly Span AsSpan() - { - return Entries == null ? Span.Empty : Entries.AsSpan(0, Length); - } - } - - private readonly Bucket[] _hashTable = new Bucket[TotalBuckets]; - - public IEnumerable Keys - { - get - { - foreach (Bucket bucket in _hashTable) - { - for (int i = 0; i < bucket.Length; i++) - { - yield return bucket.Entries[i].Key; - } - } - } - } - - public IEnumerable Values - { - get - { - foreach (Bucket bucket in _hashTable) - { - for (int i = 0; i < bucket.Length; i++) - { - yield return bucket.Entries[i].Value; - } - } - } - } - - public void Add(ref TKey key, TValue value) - { - Entry entry = new() - { - Hash = key.GetHashCode(), - Key = key, - Value = value, - }; - - int hashCode = key.GetHashCode(); - int bucketIndex = hashCode & TotalBucketsMask; - - ref Bucket bucket = ref _hashTable[bucketIndex]; - if (bucket.Entries != null) - { - int index = bucket.Length; - - if (index >= bucket.Entries.Length) - { - Array.Resize(ref bucket.Entries, index + 1); - } - - bucket.Entries[index] = entry; - } - else - { - bucket.Entries = - [ - entry - ]; - } - - bucket.Length++; - } - - public bool Remove(ref TKey key) - { - int hashCode = key.GetHashCode(); - - ref Bucket bucket = ref _hashTable[hashCode & TotalBucketsMask]; - Span entries = bucket.AsSpan(); - for (int i = 0; i < entries.Length; i++) - { - ref Entry entry = ref entries[i]; - - if (entry.Hash == hashCode && entry.Key.Equals(ref key)) - { - entries[(i + 1)..].CopyTo(entries[i..]); - bucket.Length--; - - return true; - } - } - - return false; - } - - public bool TryGetValue(ref TKey key, out TValue value) - { - int hashCode = key.GetHashCode(); - - Span entries = _hashTable[hashCode & TotalBucketsMask].AsSpan(); - for (int i = 0; i < entries.Length; i++) - { - ref Entry entry = ref entries[i]; - - if (entry.Hash == hashCode && entry.Key.Equals(ref key)) - { - value = entry.Value; - return true; - } - } - - value = default; - return false; - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/HelperShader.cs b/src/Ryujinx.Graphics.Metal/HelperShader.cs deleted file mode 100644 index 2638a6eac..000000000 --- a/src/Ryujinx.Graphics.Metal/HelperShader.cs +++ /dev/null @@ -1,868 +0,0 @@ -using Ryujinx.Common; -using Ryujinx.Common.Logging; -using Ryujinx.Graphics.GAL; -using Ryujinx.Graphics.Shader; -using Ryujinx.Graphics.Shader.Translation; -using SharpMetal.Metal; -using System; -using System.Collections.Generic; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - class HelperShader : IDisposable - { - private const int ConvertElementsPerWorkgroup = 32 * 100; // Work group size of 32 times 100 elements. - private const string ShadersSourcePath = "/Ryujinx.Graphics.Metal/Shaders"; - private readonly MetalRenderer _renderer; - private readonly Pipeline _pipeline; - private MTLDevice _device; - - private readonly ISampler _samplerLinear; - private readonly ISampler _samplerNearest; - private readonly IProgram _programColorBlitF; - private readonly IProgram _programColorBlitI; - private readonly IProgram _programColorBlitU; - private readonly IProgram _programColorBlitMsF; - private readonly IProgram _programColorBlitMsI; - private readonly IProgram _programColorBlitMsU; - private readonly List _programsColorClearF = []; - private readonly List _programsColorClearI = []; - private readonly List _programsColorClearU = []; - private readonly IProgram _programDepthStencilClear; - private readonly IProgram _programStrideChange; - private readonly IProgram _programConvertD32S8ToD24S8; - private readonly IProgram _programConvertIndexBuffer; - private readonly IProgram _programDepthBlit; - private readonly IProgram _programDepthBlitMs; - private readonly IProgram _programStencilBlit; - private readonly IProgram _programStencilBlitMs; - - private readonly EncoderState _helperShaderState = new(); - - public HelperShader(MTLDevice device, MetalRenderer renderer, Pipeline pipeline) - { - _device = device; - _renderer = renderer; - _pipeline = pipeline; - - _samplerNearest = new SamplerHolder(renderer, _device, SamplerCreateInfo.Create(MinFilter.Nearest, MagFilter.Nearest)); - _samplerLinear = new SamplerHolder(renderer, _device, SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear)); - - ResourceLayout blitResourceLayout = new ResourceLayoutBuilder() - .Add(ResourceStages.Vertex, ResourceType.UniformBuffer, 0) - .Add(ResourceStages.Fragment, ResourceType.TextureAndSampler, 0).Build(); - - string blitSource = ReadMsl("Blit.metal"); - - string blitSourceF = blitSource.Replace("FORMAT", "float", StringComparison.Ordinal); - _programColorBlitF = new Program(renderer, device, [ - new ShaderSource(blitSourceF, ShaderStage.Fragment, TargetLanguage.Msl), - new ShaderSource(blitSourceF, ShaderStage.Vertex, TargetLanguage.Msl) - ], blitResourceLayout); - - string blitSourceI = blitSource.Replace("FORMAT", "int"); - _programColorBlitI = new Program(renderer, device, [ - new ShaderSource(blitSourceI, ShaderStage.Fragment, TargetLanguage.Msl), - new ShaderSource(blitSourceI, ShaderStage.Vertex, TargetLanguage.Msl) - ], blitResourceLayout); - - string blitSourceU = blitSource.Replace("FORMAT", "uint"); - _programColorBlitU = new Program(renderer, device, [ - new ShaderSource(blitSourceU, ShaderStage.Fragment, TargetLanguage.Msl), - new ShaderSource(blitSourceU, ShaderStage.Vertex, TargetLanguage.Msl) - ], blitResourceLayout); - - string blitMsSource = ReadMsl("BlitMs.metal"); - - string blitMsSourceF = blitMsSource.Replace("FORMAT", "float"); - _programColorBlitMsF = new Program(renderer, device, [ - new ShaderSource(blitMsSourceF, ShaderStage.Fragment, TargetLanguage.Msl), - new ShaderSource(blitMsSourceF, ShaderStage.Vertex, TargetLanguage.Msl) - ], blitResourceLayout); - - string blitMsSourceI = blitMsSource.Replace("FORMAT", "int"); - _programColorBlitMsI = new Program(renderer, device, [ - new ShaderSource(blitMsSourceI, ShaderStage.Fragment, TargetLanguage.Msl), - new ShaderSource(blitMsSourceI, ShaderStage.Vertex, TargetLanguage.Msl) - ], blitResourceLayout); - - string blitMsSourceU = blitMsSource.Replace("FORMAT", "uint"); - _programColorBlitMsU = new Program(renderer, device, [ - new ShaderSource(blitMsSourceU, ShaderStage.Fragment, TargetLanguage.Msl), - new ShaderSource(blitMsSourceU, ShaderStage.Vertex, TargetLanguage.Msl) - ], blitResourceLayout); - - ResourceLayout colorClearResourceLayout = new ResourceLayoutBuilder() - .Add(ResourceStages.Fragment, ResourceType.UniformBuffer, 0).Build(); - - string colorClearSource = ReadMsl("ColorClear.metal"); - - for (int i = 0; i < Constants.MaxColorAttachments; i++) - { - string crntSource = colorClearSource.Replace("COLOR_ATTACHMENT_INDEX", i.ToString()).Replace("FORMAT", "float"); - _programsColorClearF.Add(new Program(renderer, device, [ - new ShaderSource(crntSource, ShaderStage.Fragment, TargetLanguage.Msl), - new ShaderSource(crntSource, ShaderStage.Vertex, TargetLanguage.Msl) - ], colorClearResourceLayout)); - } - - for (int i = 0; i < Constants.MaxColorAttachments; i++) - { - string crntSource = colorClearSource.Replace("COLOR_ATTACHMENT_INDEX", i.ToString()).Replace("FORMAT", "int"); - _programsColorClearI.Add(new Program(renderer, device, [ - new ShaderSource(crntSource, ShaderStage.Fragment, TargetLanguage.Msl), - new ShaderSource(crntSource, ShaderStage.Vertex, TargetLanguage.Msl) - ], colorClearResourceLayout)); - } - - for (int i = 0; i < Constants.MaxColorAttachments; i++) - { - string crntSource = colorClearSource.Replace("COLOR_ATTACHMENT_INDEX", i.ToString()).Replace("FORMAT", "uint"); - _programsColorClearU.Add(new Program(renderer, device, [ - new ShaderSource(crntSource, ShaderStage.Fragment, TargetLanguage.Msl), - new ShaderSource(crntSource, ShaderStage.Vertex, TargetLanguage.Msl) - ], colorClearResourceLayout)); - } - - string depthStencilClearSource = ReadMsl("DepthStencilClear.metal"); - _programDepthStencilClear = new Program(renderer, device, [ - new ShaderSource(depthStencilClearSource, ShaderStage.Fragment, TargetLanguage.Msl), - new ShaderSource(depthStencilClearSource, ShaderStage.Vertex, TargetLanguage.Msl) - ], colorClearResourceLayout); - - ResourceLayout strideChangeResourceLayout = new ResourceLayoutBuilder() - .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0) - .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1) - .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2, true).Build(); - - string strideChangeSource = ReadMsl("ChangeBufferStride.metal"); - _programStrideChange = new Program(renderer, device, [ - new ShaderSource(strideChangeSource, ShaderStage.Compute, TargetLanguage.Msl) - ], strideChangeResourceLayout, new ComputeSize(64, 1, 1)); - - ResourceLayout convertD32S8ToD24S8ResourceLayout = new ResourceLayoutBuilder() - .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0) - .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1) - .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2, true).Build(); - - string convertD32S8ToD24S8Source = ReadMsl("ConvertD32S8ToD24S8.metal"); - _programConvertD32S8ToD24S8 = new Program(renderer, device, [ - new ShaderSource(convertD32S8ToD24S8Source, ShaderStage.Compute, TargetLanguage.Msl) - ], convertD32S8ToD24S8ResourceLayout, new ComputeSize(64, 1, 1)); - - ResourceLayout convertIndexBufferLayout = new ResourceLayoutBuilder() - .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1) - .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2, true) - .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 3).Build(); - - string convertIndexBufferSource = ReadMsl("ConvertIndexBuffer.metal"); - _programConvertIndexBuffer = new Program(renderer, device, [ - new ShaderSource(convertIndexBufferSource, ShaderStage.Compute, TargetLanguage.Msl) - ], convertIndexBufferLayout, new ComputeSize(16, 1, 1)); - - string depthBlitSource = ReadMsl("DepthBlit.metal"); - _programDepthBlit = new Program(renderer, device, [ - new ShaderSource(depthBlitSource, ShaderStage.Fragment, TargetLanguage.Msl), - new ShaderSource(blitSourceF, ShaderStage.Vertex, TargetLanguage.Msl) - ], blitResourceLayout); - - string depthBlitMsSource = ReadMsl("DepthBlitMs.metal"); - _programDepthBlitMs = new Program(renderer, device, [ - new ShaderSource(depthBlitMsSource, ShaderStage.Fragment, TargetLanguage.Msl), - new ShaderSource(blitSourceF, ShaderStage.Vertex, TargetLanguage.Msl) - ], blitResourceLayout); - - string stencilBlitSource = ReadMsl("StencilBlit.metal"); - _programStencilBlit = new Program(renderer, device, [ - new ShaderSource(stencilBlitSource, ShaderStage.Fragment, TargetLanguage.Msl), - new ShaderSource(blitSourceF, ShaderStage.Vertex, TargetLanguage.Msl) - ], blitResourceLayout); - - string stencilBlitMsSource = ReadMsl("StencilBlitMs.metal"); - _programStencilBlitMs = new Program(renderer, device, [ - new ShaderSource(stencilBlitMsSource, ShaderStage.Fragment, TargetLanguage.Msl), - new ShaderSource(blitSourceF, ShaderStage.Vertex, TargetLanguage.Msl) - ], blitResourceLayout); - } - - private static string ReadMsl(string fileName) - { - string msl = EmbeddedResources.ReadAllText(string.Join('/', ShadersSourcePath, fileName)); - -#pragma warning disable IDE0055 // Disable formatting - msl = msl.Replace("CONSTANT_BUFFERS_INDEX", $"{Constants.ConstantBuffersIndex}") - .Replace("STORAGE_BUFFERS_INDEX", $"{Constants.StorageBuffersIndex}") - .Replace("TEXTURES_INDEX", $"{Constants.TexturesIndex}") - .Replace("IMAGES_INDEX", $"{Constants.ImagesIndex}"); -#pragma warning restore IDE0055 - - return msl; - } - - public unsafe void BlitColor( - CommandBufferScoped cbs, - Texture src, - Texture dst, - Extents2D srcRegion, - Extents2D dstRegion, - bool linearFilter, - bool clear = false) - { - _pipeline.SwapState(_helperShaderState); - - const int RegionBufferSize = 16; - - ISampler sampler = linearFilter ? _samplerLinear : _samplerNearest; - - _pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, src, sampler); - - Span region = stackalloc float[RegionBufferSize / sizeof(float)]; - - region[0] = srcRegion.X1 / (float)src.Width; - region[1] = srcRegion.X2 / (float)src.Width; - region[2] = srcRegion.Y1 / (float)src.Height; - region[3] = srcRegion.Y2 / (float)src.Height; - - if (dstRegion.X1 > dstRegion.X2) - { - (region[0], region[1]) = (region[1], region[0]); - } - - if (dstRegion.Y1 > dstRegion.Y2) - { - (region[2], region[3]) = (region[3], region[2]); - } - - using ScopedTemporaryBuffer buffer = _renderer.BufferManager.ReserveOrCreate(cbs, RegionBufferSize); - buffer.Holder.SetDataUnchecked(buffer.Offset, region); - _pipeline.SetUniformBuffers([new BufferAssignment(0, buffer.Range)]); - - Rectangle rect = new( - MathF.Min(dstRegion.X1, dstRegion.X2), - MathF.Min(dstRegion.Y1, dstRegion.Y2), - MathF.Abs(dstRegion.X2 - dstRegion.X1), - MathF.Abs(dstRegion.Y2 - dstRegion.Y1)); - - Span viewports = stackalloc Viewport[16]; - - viewports[0] = new Viewport( - rect, - ViewportSwizzle.PositiveX, - ViewportSwizzle.PositiveY, - ViewportSwizzle.PositiveZ, - ViewportSwizzle.PositiveW, - 0f, - 1f); - - bool dstIsDepthOrStencil = dst.Info.Format.IsDepthOrStencil(); - - if (dstIsDepthOrStencil) - { - // TODO: Depth & stencil blit! - Logger.Warning?.PrintMsg(LogClass.Gpu, "Requested a depth or stencil blit!"); - _pipeline.SwapState(null); - return; - } - - string debugGroupName = "Blit Color "; - - if (src.Info.Target.IsMultisample()) - { - if (dst.Info.Format.IsSint()) - { - debugGroupName += "MS Int"; - _pipeline.SetProgram(_programColorBlitMsI); - } - else if (dst.Info.Format.IsUint()) - { - debugGroupName += "MS UInt"; - _pipeline.SetProgram(_programColorBlitMsU); - } - else - { - debugGroupName += "MS Float"; - _pipeline.SetProgram(_programColorBlitMsF); - } - } - else - { - if (dst.Info.Format.IsSint()) - { - debugGroupName += "Int"; - _pipeline.SetProgram(_programColorBlitI); - } - else if (dst.Info.Format.IsUint()) - { - debugGroupName += "UInt"; - _pipeline.SetProgram(_programColorBlitU); - } - else - { - debugGroupName += "Float"; - _pipeline.SetProgram(_programColorBlitF); - } - } - - int dstWidth = dst.Width; - int dstHeight = dst.Height; - - Span> scissors = stackalloc Rectangle[16]; - - scissors[0] = new Rectangle(0, 0, dstWidth, dstHeight); - - _pipeline.SetRenderTargets([dst], null); - _pipeline.SetScissors(scissors); - - _pipeline.SetClearLoadAction(clear); - - _pipeline.SetViewports(viewports); - _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); - _pipeline.Draw(4, 1, 0, 0, debugGroupName); - - // Cleanup - if (clear) - { - _pipeline.SetClearLoadAction(false); - } - - // Restore previous state - _pipeline.SwapState(null); - } - - public unsafe void BlitDepthStencil( - CommandBufferScoped cbs, - Texture src, - Texture dst, - Extents2D srcRegion, - Extents2D dstRegion) - { - _pipeline.SwapState(_helperShaderState); - - const int RegionBufferSize = 16; - - Span region = stackalloc float[RegionBufferSize / sizeof(float)]; - - region[0] = srcRegion.X1 / (float)src.Width; - region[1] = srcRegion.X2 / (float)src.Width; - region[2] = srcRegion.Y1 / (float)src.Height; - region[3] = srcRegion.Y2 / (float)src.Height; - - if (dstRegion.X1 > dstRegion.X2) - { - (region[0], region[1]) = (region[1], region[0]); - } - - if (dstRegion.Y1 > dstRegion.Y2) - { - (region[2], region[3]) = (region[3], region[2]); - } - - using ScopedTemporaryBuffer buffer = _renderer.BufferManager.ReserveOrCreate(cbs, RegionBufferSize); - buffer.Holder.SetDataUnchecked(buffer.Offset, region); - _pipeline.SetUniformBuffers([new BufferAssignment(0, buffer.Range)]); - - Span viewports = stackalloc Viewport[16]; - - Rectangle rect = new( - MathF.Min(dstRegion.X1, dstRegion.X2), - MathF.Min(dstRegion.Y1, dstRegion.Y2), - MathF.Abs(dstRegion.X2 - dstRegion.X1), - MathF.Abs(dstRegion.Y2 - dstRegion.Y1)); - - viewports[0] = new Viewport( - rect, - ViewportSwizzle.PositiveX, - ViewportSwizzle.PositiveY, - ViewportSwizzle.PositiveZ, - ViewportSwizzle.PositiveW, - 0f, - 1f); - - int dstWidth = dst.Width; - int dstHeight = dst.Height; - - Span> scissors = stackalloc Rectangle[16]; - - scissors[0] = new Rectangle(0, 0, dstWidth, dstHeight); - - _pipeline.SetRenderTargets([], dst); - _pipeline.SetScissors(scissors); - _pipeline.SetViewports(viewports); - _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); - - if (src.Info.Format is - Format.D16Unorm or - Format.D32Float or - Format.X8UintD24Unorm or - Format.D24UnormS8Uint or - Format.D32FloatS8Uint or - Format.S8UintD24Unorm) - { - Texture depthTexture = CreateDepthOrStencilView(src, DepthStencilMode.Depth); - - BlitDepthStencilDraw(depthTexture, isDepth: true); - - if (depthTexture != src) - { - depthTexture.Release(); - } - } - - if (src.Info.Format is - Format.S8Uint or - Format.D24UnormS8Uint or - Format.D32FloatS8Uint or - Format.S8UintD24Unorm) - { - Texture stencilTexture = CreateDepthOrStencilView(src, DepthStencilMode.Stencil); - - BlitDepthStencilDraw(stencilTexture, isDepth: false); - - if (stencilTexture != src) - { - stencilTexture.Release(); - } - } - - // Restore previous state - _pipeline.SwapState(null); - } - - private static Texture CreateDepthOrStencilView(Texture depthStencilTexture, DepthStencilMode depthStencilMode) - { - if (depthStencilTexture.Info.DepthStencilMode == depthStencilMode) - { - return depthStencilTexture; - } - - return (Texture)depthStencilTexture.CreateView(new TextureCreateInfo( - depthStencilTexture.Info.Width, - depthStencilTexture.Info.Height, - depthStencilTexture.Info.Depth, - depthStencilTexture.Info.Levels, - depthStencilTexture.Info.Samples, - depthStencilTexture.Info.BlockWidth, - depthStencilTexture.Info.BlockHeight, - depthStencilTexture.Info.BytesPerPixel, - depthStencilTexture.Info.Format, - depthStencilMode, - depthStencilTexture.Info.Target, - SwizzleComponent.Red, - SwizzleComponent.Green, - SwizzleComponent.Blue, - SwizzleComponent.Alpha), 0, 0); - } - - private void BlitDepthStencilDraw(Texture src, bool isDepth) - { - // TODO: Check this https://github.com/Ryujinx/Ryujinx/pull/5003/ - _pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, src, _samplerNearest); - - string debugGroupName; - - if (isDepth) - { - debugGroupName = "Depth Blit"; - _pipeline.SetProgram(src.Info.Target.IsMultisample() ? _programDepthBlitMs : _programDepthBlit); - _pipeline.SetDepthTest(new DepthTestDescriptor(true, true, CompareOp.Always)); - } - else - { - debugGroupName = "Stencil Blit"; - _pipeline.SetProgram(src.Info.Target.IsMultisample() ? _programStencilBlitMs : _programStencilBlit); - _pipeline.SetStencilTest(CreateStencilTestDescriptor(true)); - } - - _pipeline.Draw(4, 1, 0, 0, debugGroupName); - - if (isDepth) - { - _pipeline.SetDepthTest(new DepthTestDescriptor(false, false, CompareOp.Always)); - } - else - { - _pipeline.SetStencilTest(CreateStencilTestDescriptor(false)); - } - } - - public unsafe void DrawTexture( - ITexture src, - ISampler srcSampler, - Extents2DF srcRegion, - Extents2DF dstRegion) - { - // Save current state - PredrawState state = _pipeline.SavePredrawState(); - - _pipeline.SetFaceCulling(false, Face.Front); - _pipeline.SetStencilTest(new StencilTestDescriptor()); - _pipeline.SetDepthTest(new DepthTestDescriptor()); - - const int RegionBufferSize = 16; - - _pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, src, srcSampler); - - Span region = stackalloc float[RegionBufferSize / sizeof(float)]; - - region[0] = srcRegion.X1 / src.Width; - region[1] = srcRegion.X2 / src.Width; - region[2] = srcRegion.Y1 / src.Height; - region[3] = srcRegion.Y2 / src.Height; - - if (dstRegion.X1 > dstRegion.X2) - { - (region[0], region[1]) = (region[1], region[0]); - } - - if (dstRegion.Y1 > dstRegion.Y2) - { - (region[2], region[3]) = (region[3], region[2]); - } - - BufferHandle bufferHandle = _renderer.BufferManager.CreateWithHandle(RegionBufferSize); - _renderer.BufferManager.SetData(bufferHandle, 0, region); - _pipeline.SetUniformBuffers([new BufferAssignment(0, new BufferRange(bufferHandle, 0, RegionBufferSize))]); - - Span viewports = stackalloc Viewport[16]; - - Rectangle rect = new( - MathF.Min(dstRegion.X1, dstRegion.X2), - MathF.Min(dstRegion.Y1, dstRegion.Y2), - MathF.Abs(dstRegion.X2 - dstRegion.X1), - MathF.Abs(dstRegion.Y2 - dstRegion.Y1)); - - viewports[0] = new Viewport( - rect, - ViewportSwizzle.PositiveX, - ViewportSwizzle.PositiveY, - ViewportSwizzle.PositiveZ, - ViewportSwizzle.PositiveW, - 0f, - 1f); - - _pipeline.SetProgram(_programColorBlitF); - _pipeline.SetViewports(viewports); - _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); - _pipeline.Draw(4, 1, 0, 0, "Draw Texture"); - - _renderer.BufferManager.Delete(bufferHandle); - - // Restore previous state - _pipeline.RestorePredrawState(state); - } - - public void ConvertI8ToI16(CommandBufferScoped cbs, BufferHolder src, BufferHolder dst, int srcOffset, int size) - { - ChangeStride(cbs, src, dst, srcOffset, size, 1, 2); - } - - public unsafe void ChangeStride( - CommandBufferScoped cbs, - BufferHolder src, - BufferHolder dst, - int srcOffset, - int size, - int stride, - int newStride) - { - int elems = size / stride; - - Auto srcBuffer = src.GetBuffer(); - Auto dstBuffer = dst.GetBuffer(); - - const int ParamsBufferSize = 4 * sizeof(int); - - // Save current state - _pipeline.SwapState(_helperShaderState); - - Span shaderParams = stackalloc int[ParamsBufferSize / sizeof(int)]; - - shaderParams[0] = stride; - shaderParams[1] = newStride; - shaderParams[2] = size; - shaderParams[3] = srcOffset; - - using ScopedTemporaryBuffer buffer = _renderer.BufferManager.ReserveOrCreate(cbs, ParamsBufferSize); - buffer.Holder.SetDataUnchecked(buffer.Offset, shaderParams); - _pipeline.SetUniformBuffers([new BufferAssignment(0, buffer.Range)]); - - Span> sbRanges = new Auto[2]; - - sbRanges[0] = srcBuffer; - sbRanges[1] = dstBuffer; - _pipeline.SetStorageBuffers(1, sbRanges); - - _pipeline.SetProgram(_programStrideChange); - _pipeline.DispatchCompute(1 + elems / ConvertElementsPerWorkgroup, 1, 1, "Change Stride"); - - // Restore previous state - _pipeline.SwapState(null); - } - - public unsafe void ConvertD32S8ToD24S8(CommandBufferScoped cbs, BufferHolder src, Auto dstBuffer, int pixelCount, int dstOffset) - { - int inSize = pixelCount * 2 * sizeof(int); - - Auto srcBuffer = src.GetBuffer(); - - const int ParamsBufferSize = sizeof(int) * 2; - - // Save current state - _pipeline.SwapState(_helperShaderState); - - Span shaderParams = stackalloc int[2]; - - shaderParams[0] = pixelCount; - shaderParams[1] = dstOffset; - - using ScopedTemporaryBuffer buffer = _renderer.BufferManager.ReserveOrCreate(cbs, ParamsBufferSize); - buffer.Holder.SetDataUnchecked(buffer.Offset, shaderParams); - _pipeline.SetUniformBuffers([new BufferAssignment(0, buffer.Range)]); - - Span> sbRanges = new Auto[2]; - - sbRanges[0] = srcBuffer; - sbRanges[1] = dstBuffer; - _pipeline.SetStorageBuffers(1, sbRanges); - - _pipeline.SetProgram(_programConvertD32S8ToD24S8); - _pipeline.DispatchCompute(1 + inSize / ConvertElementsPerWorkgroup, 1, 1, "D32S8 to D24S8 Conversion"); - - // Restore previous state - _pipeline.SwapState(null); - } - - public void ConvertIndexBuffer( - CommandBufferScoped cbs, - BufferHolder src, - BufferHolder dst, - IndexBufferPattern pattern, - int indexSize, - int srcOffset, - int indexCount) - { - // TODO: Support conversion with primitive restart enabled. - - int primitiveCount = pattern.GetPrimitiveCount(indexCount); - int outputIndexSize = 4; - - Auto srcBuffer = src.GetBuffer(); - Auto dstBuffer = dst.GetBuffer(); - - const int ParamsBufferSize = 16 * sizeof(int); - - // Save current state - _pipeline.SwapState(_helperShaderState); - - Span shaderParams = stackalloc int[ParamsBufferSize / sizeof(int)]; - - shaderParams[8] = pattern.PrimitiveVertices; - shaderParams[9] = pattern.PrimitiveVerticesOut; - shaderParams[10] = indexSize; - shaderParams[11] = outputIndexSize; - shaderParams[12] = pattern.BaseIndex; - shaderParams[13] = pattern.IndexStride; - shaderParams[14] = srcOffset; - shaderParams[15] = primitiveCount; - - pattern.OffsetIndex.CopyTo(shaderParams[..pattern.OffsetIndex.Length]); - - using ScopedTemporaryBuffer patternScoped = _renderer.BufferManager.ReserveOrCreate(cbs, ParamsBufferSize); - patternScoped.Holder.SetDataUnchecked(patternScoped.Offset, shaderParams); - - Span> sbRanges = new Auto[2]; - - sbRanges[0] = srcBuffer; - sbRanges[1] = dstBuffer; - _pipeline.SetStorageBuffers(1, sbRanges); - _pipeline.SetStorageBuffers([new BufferAssignment(3, patternScoped.Range)]); - - _pipeline.SetProgram(_programConvertIndexBuffer); - _pipeline.DispatchCompute(BitUtils.DivRoundUp(primitiveCount, 16), 1, 1, "Convert Index Buffer"); - - // Restore previous state - _pipeline.SwapState(null); - } - - public unsafe void ClearColor( - int index, - ReadOnlySpan clearColor, - uint componentMask, - int dstWidth, - int dstHeight, - Format format) - { - // Keep original scissor - DirtyFlags clearFlags = DirtyFlags.All & (~DirtyFlags.Scissors); - - // Save current state - EncoderState originalState = _pipeline.SwapState(_helperShaderState, clearFlags, false); - - // Inherit some state without fully recreating render pipeline. - RenderTargetCopy save = _helperShaderState.InheritForClear(originalState, false, index); - - const int ClearColorBufferSize = 16; - - // TODO: Flush - - using ScopedTemporaryBuffer buffer = _renderer.BufferManager.ReserveOrCreate(_pipeline.Cbs, ClearColorBufferSize); - buffer.Holder.SetDataUnchecked(buffer.Offset, clearColor); - _pipeline.SetUniformBuffers([new BufferAssignment(0, buffer.Range)]); - - Span viewports = stackalloc Viewport[16]; - - // TODO: Set exact viewport! - viewports[0] = new Viewport( - new Rectangle(0, 0, dstWidth, dstHeight), - ViewportSwizzle.PositiveX, - ViewportSwizzle.PositiveY, - ViewportSwizzle.PositiveZ, - ViewportSwizzle.PositiveW, - 0f, - 1f); - - Span componentMasks = stackalloc uint[index + 1]; - componentMasks[index] = componentMask; - - string debugGroupName = "Clear Color "; - - if (format.IsSint()) - { - debugGroupName += "Int"; - _pipeline.SetProgram(_programsColorClearI[index]); - } - else if (format.IsUint()) - { - debugGroupName += "UInt"; - _pipeline.SetProgram(_programsColorClearU[index]); - } - else - { - debugGroupName += "Float"; - _pipeline.SetProgram(_programsColorClearF[index]); - } - - _pipeline.SetBlendState(index, new BlendDescriptor()); - _pipeline.SetFaceCulling(false, Face.Front); - _pipeline.SetDepthTest(new DepthTestDescriptor(false, false, CompareOp.Always)); - _pipeline.SetRenderTargetColorMasks(componentMasks); - _pipeline.SetViewports(viewports); - _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); - _pipeline.Draw(4, 1, 0, 0, debugGroupName); - - // Restore previous state - _pipeline.SwapState(null, clearFlags, false); - - _helperShaderState.Restore(save); - } - - public unsafe void ClearDepthStencil( - float depthValue, - bool depthMask, - int stencilValue, - int stencilMask, - int dstWidth, - int dstHeight) - { - // Keep original scissor - DirtyFlags clearFlags = DirtyFlags.All & (~DirtyFlags.Scissors); - MTLScissorRect[] helperScissors = _helperShaderState.Scissors; - - // Save current state - EncoderState originalState = _pipeline.SwapState(_helperShaderState, clearFlags, false); - - // Inherit some state without fully recreating render pipeline. - RenderTargetCopy save = _helperShaderState.InheritForClear(originalState, true); - - const int ClearDepthBufferSize = 16; - - using ScopedTemporaryBuffer buffer = _renderer.BufferManager.ReserveOrCreate(_pipeline.Cbs, ClearDepthBufferSize); - buffer.Holder.SetDataUnchecked(buffer.Offset, new ReadOnlySpan(ref depthValue)); - _pipeline.SetUniformBuffers([new BufferAssignment(0, buffer.Range)]); - - Span viewports = stackalloc Viewport[1]; - - viewports[0] = new Viewport( - new Rectangle(0, 0, dstWidth, dstHeight), - ViewportSwizzle.PositiveX, - ViewportSwizzle.PositiveY, - ViewportSwizzle.PositiveZ, - ViewportSwizzle.PositiveW, - 0f, - 1f); - - _pipeline.SetProgram(_programDepthStencilClear); - _pipeline.SetFaceCulling(false, Face.Front); - _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); - _pipeline.SetViewports(viewports); - _pipeline.SetDepthTest(new DepthTestDescriptor(true, depthMask, CompareOp.Always)); - _pipeline.SetStencilTest(CreateStencilTestDescriptor(stencilMask != 0, stencilValue, 0xFF, stencilMask)); - _pipeline.Draw(4, 1, 0, 0, "Clear Depth Stencil"); - - // Cleanup - _pipeline.SetDepthTest(new DepthTestDescriptor(false, false, CompareOp.Always)); - _pipeline.SetStencilTest(CreateStencilTestDescriptor(false)); - - // Restore previous state - _pipeline.SwapState(null, clearFlags, false); - - _helperShaderState.Restore(save); - } - - private static StencilTestDescriptor CreateStencilTestDescriptor( - bool enabled, - int refValue = 0, - int compareMask = 0xff, - int writeMask = 0xff) - { - return new StencilTestDescriptor( - enabled, - CompareOp.Always, - StencilOp.Replace, - StencilOp.Replace, - StencilOp.Replace, - refValue, - compareMask, - writeMask, - CompareOp.Always, - StencilOp.Replace, - StencilOp.Replace, - StencilOp.Replace, - refValue, - compareMask, - writeMask); - } - - public void Dispose() - { - _programColorBlitF.Dispose(); - _programColorBlitI.Dispose(); - _programColorBlitU.Dispose(); - _programColorBlitMsF.Dispose(); - _programColorBlitMsI.Dispose(); - _programColorBlitMsU.Dispose(); - - foreach (IProgram programColorClear in _programsColorClearF) - { - programColorClear.Dispose(); - } - - foreach (IProgram programColorClear in _programsColorClearU) - { - programColorClear.Dispose(); - } - - foreach (IProgram programColorClear in _programsColorClearI) - { - programColorClear.Dispose(); - } - - _programDepthStencilClear.Dispose(); - _pipeline.Dispose(); - _samplerLinear.Dispose(); - _samplerNearest.Dispose(); - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/IdList.cs b/src/Ryujinx.Graphics.Metal/IdList.cs deleted file mode 100644 index 72c9d5fcc..000000000 --- a/src/Ryujinx.Graphics.Metal/IdList.cs +++ /dev/null @@ -1,121 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Ryujinx.Graphics.Metal -{ - class IdList where T : class - { - private readonly List _list; - private int _freeMin; - - public IdList() - { - _list = []; - _freeMin = 0; - } - - public int Add(T value) - { - int id; - int count = _list.Count; - id = _list.IndexOf(null, _freeMin); - - if ((uint)id < (uint)count) - { - _list[id] = value; - } - else - { - id = count; - _freeMin = id + 1; - - _list.Add(value); - } - - return id + 1; - } - - public void Remove(int id) - { - id--; - - int count = _list.Count; - - if ((uint)id >= (uint)count) - { - return; - } - - if (id + 1 == count) - { - // Trim unused items. - int removeIndex = id; - - while (removeIndex > 0 && _list[removeIndex - 1] == null) - { - removeIndex--; - } - - _list.RemoveRange(removeIndex, count - removeIndex); - - if (_freeMin > removeIndex) - { - _freeMin = removeIndex; - } - } - else - { - _list[id] = null; - - if (_freeMin > id) - { - _freeMin = id; - } - } - } - - public bool TryGetValue(int id, out T value) - { - id--; - - try - { - if ((uint)id < (uint)_list.Count) - { - value = _list[id]; - return value != null; - } - - value = null; - return false; - } - catch (ArgumentOutOfRangeException) - { - value = null; - return false; - } - catch (IndexOutOfRangeException) - { - value = null; - return false; - } - } - - public void Clear() - { - _list.Clear(); - _freeMin = 0; - } - - public IEnumerator GetEnumerator() - { - for (int i = 0; i < _list.Count; i++) - { - if (_list[i] != null) - { - yield return _list[i]; - } - } - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/ImageArray.cs b/src/Ryujinx.Graphics.Metal/ImageArray.cs deleted file mode 100644 index 9fa0df09d..000000000 --- a/src/Ryujinx.Graphics.Metal/ImageArray.cs +++ /dev/null @@ -1,74 +0,0 @@ -using Ryujinx.Graphics.GAL; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - internal class ImageArray : IImageArray - { - private readonly TextureRef[] _textureRefs; - private readonly TextureBuffer[] _bufferTextureRefs; - - private readonly bool _isBuffer; - private readonly Pipeline _pipeline; - - public ImageArray(int size, bool isBuffer, Pipeline pipeline) - { - if (isBuffer) - { - _bufferTextureRefs = new TextureBuffer[size]; - } - else - { - _textureRefs = new TextureRef[size]; - } - - _isBuffer = isBuffer; - _pipeline = pipeline; - } - - public void SetImages(int index, ITexture[] images) - { - for (int i = 0; i < images.Length; i++) - { - ITexture image = images[i]; - - if (image is TextureBuffer textureBuffer) - { - _bufferTextureRefs[index + i] = textureBuffer; - } - else if (image is Texture texture) - { - _textureRefs[index + i].Storage = texture; - } - else if (!_isBuffer) - { - _textureRefs[index + i].Storage = null; - } - else - { - _bufferTextureRefs[index + i] = null; - } - } - - SetDirty(); - } - - public TextureRef[] GetTextureRefs() - { - return _textureRefs; - } - - public TextureBuffer[] GetBufferTextureRefs() - { - return _bufferTextureRefs; - } - - private void SetDirty() - { - _pipeline.DirtyImages(); - } - - public void Dispose() { } - } -} diff --git a/src/Ryujinx.Graphics.Metal/IndexBufferPattern.cs b/src/Ryujinx.Graphics.Metal/IndexBufferPattern.cs deleted file mode 100644 index 24e3222fe..000000000 --- a/src/Ryujinx.Graphics.Metal/IndexBufferPattern.cs +++ /dev/null @@ -1,118 +0,0 @@ -using Ryujinx.Graphics.GAL; -using System; -using System.Runtime.InteropServices; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - internal class IndexBufferPattern : IDisposable - { - public int PrimitiveVertices { get; } - public int PrimitiveVerticesOut { get; } - public int BaseIndex { get; } - public int[] OffsetIndex { get; } - public int IndexStride { get; } - public bool RepeatStart { get; } - - private readonly MetalRenderer _renderer; - private int _currentSize; - private BufferHandle _repeatingBuffer; - - public IndexBufferPattern(MetalRenderer renderer, - int primitiveVertices, - int primitiveVerticesOut, - int baseIndex, - int[] offsetIndex, - int indexStride, - bool repeatStart) - { - PrimitiveVertices = primitiveVertices; - PrimitiveVerticesOut = primitiveVerticesOut; - BaseIndex = baseIndex; - OffsetIndex = offsetIndex; - IndexStride = indexStride; - RepeatStart = repeatStart; - - _renderer = renderer; - } - - public int GetPrimitiveCount(int vertexCount) - { - return Math.Max(0, (vertexCount - BaseIndex) / IndexStride); - } - - public int GetConvertedCount(int indexCount) - { - int primitiveCount = GetPrimitiveCount(indexCount); - return primitiveCount * OffsetIndex.Length; - } - - public BufferHandle GetRepeatingBuffer(int vertexCount, out int indexCount) - { - int primitiveCount = GetPrimitiveCount(vertexCount); - indexCount = primitiveCount * PrimitiveVerticesOut; - - int expectedSize = primitiveCount * OffsetIndex.Length; - - if (expectedSize <= _currentSize && _repeatingBuffer != BufferHandle.Null) - { - return _repeatingBuffer; - } - - // Expand the repeating pattern to the number of requested primitives. - BufferHandle newBuffer = _renderer.BufferManager.CreateWithHandle(expectedSize * sizeof(int)); - - // Copy the old data to the new one. - if (_repeatingBuffer != BufferHandle.Null) - { - _renderer.Pipeline.CopyBuffer(_repeatingBuffer, newBuffer, 0, 0, _currentSize * sizeof(int)); - _renderer.BufferManager.Delete(_repeatingBuffer); - } - - _repeatingBuffer = newBuffer; - - // Add the additional repeats on top. - int newPrimitives = primitiveCount; - int oldPrimitives = (_currentSize) / OffsetIndex.Length; - - int[] newData; - - newPrimitives -= oldPrimitives; - newData = new int[expectedSize - _currentSize]; - - int outOffset = 0; - int index = oldPrimitives * IndexStride + BaseIndex; - - for (int i = 0; i < newPrimitives; i++) - { - if (RepeatStart) - { - // Used for triangle fan - newData[outOffset++] = 0; - } - - for (int j = RepeatStart ? 1 : 0; j < OffsetIndex.Length; j++) - { - newData[outOffset++] = index + OffsetIndex[j]; - } - - index += IndexStride; - } - - _renderer.SetBufferData(newBuffer, _currentSize * sizeof(int), MemoryMarshal.Cast(newData)); - _currentSize = expectedSize; - - return newBuffer; - } - - public void Dispose() - { - if (_repeatingBuffer != BufferHandle.Null) - { - _renderer.BufferManager.Delete(_repeatingBuffer); - _repeatingBuffer = BufferHandle.Null; - } - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/IndexBufferState.cs b/src/Ryujinx.Graphics.Metal/IndexBufferState.cs deleted file mode 100644 index 02c9ff9ef..000000000 --- a/src/Ryujinx.Graphics.Metal/IndexBufferState.cs +++ /dev/null @@ -1,103 +0,0 @@ -using Ryujinx.Graphics.GAL; -using SharpMetal.Metal; -using System; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - readonly internal struct IndexBufferState - { - public static IndexBufferState Null => new(BufferHandle.Null, 0, 0); - - private readonly int _offset; - private readonly int _size; - private readonly IndexType _type; - - private readonly BufferHandle _handle; - - public IndexBufferState(BufferHandle handle, int offset, int size, IndexType type = IndexType.UInt) - { - _handle = handle; - _offset = offset; - _size = size; - _type = type; - } - - public (MTLBuffer, int, MTLIndexType) GetIndexBuffer(MetalRenderer renderer, CommandBufferScoped cbs) - { - Auto autoBuffer; - int offset, size; - MTLIndexType type; - - if (_type == IndexType.UByte) - { - // Index type is not supported. Convert to I16. - autoBuffer = renderer.BufferManager.GetBufferI8ToI16(cbs, _handle, _offset, _size); - - type = MTLIndexType.UInt16; - offset = 0; - size = _size * 2; - } - else - { - autoBuffer = renderer.BufferManager.GetBuffer(_handle, false, out int bufferSize); - - if (_offset >= bufferSize) - { - autoBuffer = null; - } - - type = _type.Convert(); - offset = _offset; - size = _size; - } - - if (autoBuffer != null) - { - DisposableBuffer buffer = autoBuffer.Get(cbs, offset, size); - - return (buffer.Value, offset, type); - } - - return (new MTLBuffer(IntPtr.Zero), 0, MTLIndexType.UInt16); - } - - public (MTLBuffer, int, MTLIndexType) GetConvertedIndexBuffer( - MetalRenderer renderer, - CommandBufferScoped cbs, - int firstIndex, - int indexCount, - int convertedCount, - IndexBufferPattern pattern) - { - // Convert the index buffer using the given pattern. - int indexSize = GetIndexSize(); - - int firstIndexOffset = firstIndex * indexSize; - - Auto autoBuffer = renderer.BufferManager.GetBufferTopologyConversion(cbs, _handle, _offset + firstIndexOffset, indexCount * indexSize, pattern, indexSize); - - int size = convertedCount * 4; - - if (autoBuffer != null) - { - DisposableBuffer buffer = autoBuffer.Get(cbs, 0, size); - - return (buffer.Value, 0, MTLIndexType.UInt32); - } - - return (new MTLBuffer(IntPtr.Zero), 0, MTLIndexType.UInt32); - } - - private int GetIndexSize() - { - return _type switch - { - IndexType.UInt => 4, - IndexType.UShort => 2, - _ => 1, - }; - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs deleted file mode 100644 index 86e2451b3..000000000 --- a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs +++ /dev/null @@ -1,312 +0,0 @@ -using Ryujinx.Common.Configuration; -using Ryujinx.Graphics.GAL; -using Ryujinx.Graphics.Shader.Translation; -using SharpMetal.Metal; -using SharpMetal.QuartzCore; -using System; -using System.Collections.Generic; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - public sealed class MetalRenderer : IRenderer - { - public const int TotalSets = 4; - - private readonly MTLDevice _device; - private readonly MTLCommandQueue _queue; - private readonly Func _getMetalLayer; - - private Pipeline _pipeline; - private Window _window; - - public uint ProgramCount { get; set; } - -#pragma warning disable CS0067 // The event is never used - public event EventHandler ScreenCaptured; -#pragma warning restore CS0067 - - public bool PreferThreading => true; - public IPipeline Pipeline => _pipeline; - public IWindow Window => _window; - - internal MTLCommandQueue BackgroundQueue { get; private set; } - internal HelperShader HelperShader { get; private set; } - internal BufferManager BufferManager { get; private set; } - internal CommandBufferPool CommandBufferPool { get; private set; } - internal BackgroundResources BackgroundResources { get; private set; } - internal Action InterruptAction { get; private set; } - internal SyncManager SyncManager { get; private set; } - - internal HashSet Programs { get; } - internal HashSet Samplers { get; } - - public MetalRenderer(Func metalLayer) - { - _device = MTLDevice.CreateSystemDefaultDevice(); - Programs = []; - Samplers = []; - - if (_device.ArgumentBuffersSupport != MTLArgumentBuffersTier.Tier2) - { - throw new NotSupportedException("Metal backend requires Tier 2 Argument Buffer support."); - } - - _queue = _device.NewCommandQueue(CommandBufferPool.MaxCommandBuffers + 1); - BackgroundQueue = _device.NewCommandQueue(CommandBufferPool.MaxCommandBuffers); - - _getMetalLayer = metalLayer; - } - - public void Initialize(GraphicsDebugLevel logLevel) - { - CAMetalLayer layer = _getMetalLayer(); - layer.Device = _device; - layer.FramebufferOnly = false; - - CommandBufferPool = new CommandBufferPool(_queue); - _window = new Window(this, layer); - _pipeline = new Pipeline(_device, this); - BufferManager = new BufferManager(_device, this, _pipeline); - - _pipeline.InitEncoderStateManager(BufferManager); - - BackgroundResources = new BackgroundResources(this); - HelperShader = new HelperShader(_device, this, _pipeline); - SyncManager = new SyncManager(this); - } - - public void BackgroundContextAction(Action action, bool alwaysBackground = false) - { - // GetData methods should be thread safe, so we can call this directly. - // Texture copy (scaled) may also happen in here, so that should also be thread safe. - - action(); - } - - public BufferHandle CreateBuffer(int size, BufferAccess access) - { - return BufferManager.CreateWithHandle(size); - } - - public BufferHandle CreateBuffer(IntPtr pointer, int size) - { - return BufferManager.Create(pointer, size); - } - - public BufferHandle CreateBufferSparse(ReadOnlySpan storageBuffers) - { - throw new NotImplementedException(); - } - - public IImageArray CreateImageArray(int size, bool isBuffer) - { - return new ImageArray(size, isBuffer, _pipeline); - } - - public IProgram CreateProgram(ShaderSource[] shaders, ShaderInfo info) - { - ProgramCount++; - return new Program(this, _device, shaders, info.ResourceLayout, info.ComputeLocalSize); - } - - public ISampler CreateSampler(SamplerCreateInfo info) - { - return new SamplerHolder(this, _device, info); - } - - public ITexture CreateTexture(TextureCreateInfo info) - { - if (info.Target == Target.TextureBuffer) - { - return new TextureBuffer(_device, this, _pipeline, info); - } - - return new Texture(_device, this, _pipeline, info); - } - - public ITextureArray CreateTextureArray(int size, bool isBuffer) - { - return new TextureArray(size, isBuffer, _pipeline); - } - - public bool PrepareHostMapping(IntPtr address, ulong size) - { - // TODO: Metal Host Mapping - return false; - } - - public void CreateSync(ulong id, bool strict) - { - SyncManager.Create(id, strict); - } - - public void DeleteBuffer(BufferHandle buffer) - { - BufferManager.Delete(buffer); - } - - public PinnedSpan GetBufferData(BufferHandle buffer, int offset, int size) - { - return BufferManager.GetData(buffer, offset, size); - } - - public Capabilities GetCapabilities() - { - // TODO: Finalize these values - return new Capabilities( - api: TargetApi.Metal, - vendorName: HardwareInfoTools.GetVendor(), - SystemMemoryType.UnifiedMemory, - hasFrontFacingBug: false, - hasVectorIndexingBug: false, - needsFragmentOutputSpecialization: true, - reduceShaderPrecision: true, - supportsAstcCompression: true, - supportsBc123Compression: true, - supportsBc45Compression: true, - supportsBc67Compression: true, - supportsEtc2Compression: true, - supports3DTextureCompression: true, - supportsBgraFormat: true, - supportsR4G4Format: false, - supportsR4G4B4A4Format: true, - supportsScaledVertexFormats: false, - supportsSnormBufferTextureFormat: true, - supportsSparseBuffer: false, - supports5BitComponentFormat: true, - supportsBlendEquationAdvanced: false, - supportsFragmentShaderInterlock: true, - supportsFragmentShaderOrderingIntel: false, - supportsGeometryShader: false, - supportsGeometryShaderPassthrough: false, - supportsTransformFeedback: false, - supportsImageLoadFormatted: false, - supportsLayerVertexTessellation: false, - supportsMismatchingViewFormat: true, - supportsCubemapView: true, - supportsNonConstantTextureOffset: false, - supportsQuads: false, - supportsSeparateSampler: true, - supportsShaderBallot: false, - supportsShaderBarrierDivergence: false, - supportsShaderFloat64: false, - supportsTextureGatherOffsets: false, - supportsTextureShadowLod: false, - supportsVertexStoreAndAtomics: false, - supportsViewportIndexVertexTessellation: false, - supportsViewportMask: false, - supportsViewportSwizzle: false, - supportsIndirectParameters: true, - supportsDepthClipControl: false, - uniformBufferSetIndex: (int)Constants.ConstantBuffersSetIndex, - storageBufferSetIndex: (int)Constants.StorageBuffersSetIndex, - textureSetIndex: (int)Constants.TexturesSetIndex, - imageSetIndex: (int)Constants.ImagesSetIndex, - extraSetBaseIndex: TotalSets, - maximumExtraSets: (int)Constants.MaximumExtraSets, - maximumUniformBuffersPerStage: Constants.MaxUniformBuffersPerStage, - maximumStorageBuffersPerStage: Constants.MaxStorageBuffersPerStage, - maximumTexturesPerStage: Constants.MaxTexturesPerStage, - maximumImagesPerStage: Constants.MaxImagesPerStage, - maximumComputeSharedMemorySize: (int)_device.MaxThreadgroupMemoryLength, - maximumSupportedAnisotropy: 16, - shaderSubgroupSize: 256, - storageBufferOffsetAlignment: 16, - textureBufferOffsetAlignment: 16, - gatherBiasPrecision: 0, - maximumGpuMemory: 0 - ); - } - - public ulong GetCurrentSync() - { - return SyncManager.GetCurrent(); - } - - public HardwareInfo GetHardwareInfo() - { - return new HardwareInfo(HardwareInfoTools.GetVendor(), HardwareInfoTools.GetModel(), "Apple"); - } - - public IProgram LoadProgramBinary(byte[] programBinary, bool hasFragmentShader, ShaderInfo info) - { - throw new NotImplementedException(); - } - - public void SetBufferData(BufferHandle buffer, int offset, ReadOnlySpan data) - { - BufferManager.SetData(buffer, offset, data, _pipeline.Cbs); - } - - public void UpdateCounters() - { - // https://developer.apple.com/documentation/metal/gpu_counters_and_counter_sample_buffers/creating_a_counter_sample_buffer_to_store_a_gpu_s_counter_data_during_a_pass?language=objc - } - - public void PreFrame() - { - SyncManager.Cleanup(); - } - - public ICounterEvent ReportCounter(CounterType type, EventHandler resultHandler, float divisor, bool hostReserved) - { - // https://developer.apple.com/documentation/metal/gpu_counters_and_counter_sample_buffers/creating_a_counter_sample_buffer_to_store_a_gpu_s_counter_data_during_a_pass?language=objc - CounterEvent counterEvent = new(); - resultHandler?.Invoke(counterEvent, type == CounterType.SamplesPassed ? (ulong)1 : 0); - return counterEvent; - } - - public void ResetCounter(CounterType type) - { - // https://developer.apple.com/documentation/metal/gpu_counters_and_counter_sample_buffers/creating_a_counter_sample_buffer_to_store_a_gpu_s_counter_data_during_a_pass?language=objc - } - - public void WaitSync(ulong id) - { - SyncManager.Wait(id); - } - - public void FlushAllCommands() - { - _pipeline.FlushCommandsImpl(); - } - - public void RegisterFlush() - { - SyncManager.RegisterFlush(); - - // Periodically free unused regions of the staging buffer to avoid doing it all at once. - BufferManager.StagingBuffer.FreeCompleted(); - } - - public void SetInterruptAction(Action interruptAction) - { - InterruptAction = interruptAction; - } - - public void Screenshot() - { - // TODO: Screenshots - } - - public void Dispose() - { - BackgroundResources.Dispose(); - - foreach (Program program in Programs) - { - program.Dispose(); - } - - foreach (SamplerHolder sampler in Samplers) - { - sampler.Dispose(); - } - - _pipeline.Dispose(); - _window.Dispose(); - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/MultiFenceHolder.cs b/src/Ryujinx.Graphics.Metal/MultiFenceHolder.cs deleted file mode 100644 index 89ae1fa77..000000000 --- a/src/Ryujinx.Graphics.Metal/MultiFenceHolder.cs +++ /dev/null @@ -1,262 +0,0 @@ -using SharpMetal.Metal; -using System; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - /// - /// Holder for multiple host GPU fences. - /// - [SupportedOSPlatform("macos")] - class MultiFenceHolder - { - private const int BufferUsageTrackingGranularity = 4096; - - private readonly FenceHolder[] _fences; - private readonly BufferUsageBitmap _bufferUsageBitmap; - - /// - /// Creates a new instance of the multiple fence holder. - /// - public MultiFenceHolder() - { - _fences = new FenceHolder[CommandBufferPool.MaxCommandBuffers]; - } - - /// - /// Creates a new instance of the multiple fence holder, with a given buffer size in mind. - /// - /// Size of the buffer - public MultiFenceHolder(int size) - { - _fences = new FenceHolder[CommandBufferPool.MaxCommandBuffers]; - _bufferUsageBitmap = new BufferUsageBitmap(size, BufferUsageTrackingGranularity); - } - - /// - /// Adds read/write buffer usage information to the uses list. - /// - /// Index of the command buffer where the buffer is used - /// Offset of the buffer being used - /// Size of the buffer region being used, in bytes - /// Whether the access is a write or not - public void AddBufferUse(int cbIndex, int offset, int size, bool write) - { - _bufferUsageBitmap.Add(cbIndex, offset, size, false); - - if (write) - { - _bufferUsageBitmap.Add(cbIndex, offset, size, true); - } - } - - /// - /// Removes all buffer usage information for a given command buffer. - /// - /// Index of the command buffer where the buffer is used - public void RemoveBufferUses(int cbIndex) - { - _bufferUsageBitmap?.Clear(cbIndex); - } - - /// - /// Checks if a given range of a buffer is being used by a command buffer still being processed by the GPU. - /// - /// Index of the command buffer where the buffer is used - /// Offset of the buffer being used - /// Size of the buffer region being used, in bytes - /// True if in use, false otherwise - public bool IsBufferRangeInUse(int cbIndex, int offset, int size) - { - return _bufferUsageBitmap.OverlapsWith(cbIndex, offset, size); - } - - /// - /// Checks if a given range of a buffer is being used by any command buffer still being processed by the GPU. - /// - /// Offset of the buffer being used - /// Size of the buffer region being used, in bytes - /// True if only write usages should count - /// True if in use, false otherwise - public bool IsBufferRangeInUse(int offset, int size, bool write) - { - return _bufferUsageBitmap.OverlapsWith(offset, size, write); - } - - /// - /// Adds a fence to the holder. - /// - /// Command buffer index of the command buffer that owns the fence - /// Fence to be added - /// True if the command buffer's previous fence value was null - public bool AddFence(int cbIndex, FenceHolder fence) - { - ref FenceHolder fenceRef = ref _fences[cbIndex]; - - if (fenceRef == null) - { - fenceRef = fence; - return true; - } - - return false; - } - - /// - /// Removes a fence from the holder. - /// - /// Command buffer index of the command buffer that owns the fence - public void RemoveFence(int cbIndex) - { - _fences[cbIndex] = null; - } - - /// - /// Determines if a fence referenced on the given command buffer. - /// - /// Index of the command buffer to check if it's used - /// True if referenced, false otherwise - public bool HasFence(int cbIndex) - { - return _fences[cbIndex] != null; - } - - /// - /// Wait until all the fences on the holder are signaled. - /// - public void WaitForFences() - { - WaitForFencesImpl(0, 0, true); - } - - /// - /// Wait until all the fences on the holder with buffer uses overlapping the specified range are signaled. - /// - /// Start offset of the buffer range - /// Size of the buffer range in bytes - public void WaitForFences(int offset, int size) - { - WaitForFencesImpl(offset, size, true); - } - - /// - /// Wait until all the fences on the holder with buffer uses overlapping the specified range are signaled. - /// - - // TODO: Add a proper timeout! - public bool WaitForFences(bool indefinite) - { - return WaitForFencesImpl(0, 0, indefinite); - } - - /// - /// Wait until all the fences on the holder with buffer uses overlapping the specified range are signaled. - /// - /// Start offset of the buffer range - /// Size of the buffer range in bytes - /// Indicates if this should wait indefinitely - /// True if all fences were signaled before the timeout expired, false otherwise - private bool WaitForFencesImpl(int offset, int size, bool indefinite) - { - Span fenceHolders = new FenceHolder[CommandBufferPool.MaxCommandBuffers]; - - int count = size != 0 ? GetOverlappingFences(fenceHolders, offset, size) : GetFences(fenceHolders); - Span fences = stackalloc MTLCommandBuffer[count]; - - int fenceCount = 0; - - for (int i = 0; i < count; i++) - { - if (fenceHolders[i].TryGet(out MTLCommandBuffer fence)) - { - fences[fenceCount] = fence; - - if (fenceCount < i) - { - fenceHolders[fenceCount] = fenceHolders[i]; - } - - fenceCount++; - } - } - - if (fenceCount == 0) - { - return true; - } - - bool signaled = true; - - if (indefinite) - { - foreach (MTLCommandBuffer fence in fences) - { - fence.WaitUntilCompleted(); - } - } - else - { - foreach (MTLCommandBuffer fence in fences) - { - if (fence.Status != MTLCommandBufferStatus.Completed) - { - signaled = false; - } - } - } - - for (int i = 0; i < fenceCount; i++) - { - fenceHolders[i].Put(); - } - - return signaled; - } - - /// - /// Gets fences to wait for. - /// - /// Span to store fences in - /// Number of fences placed in storage - private int GetFences(Span storage) - { - int count = 0; - - for (int i = 0; i < _fences.Length; i++) - { - FenceHolder fence = _fences[i]; - - if (fence != null) - { - storage[count++] = fence; - } - } - - return count; - } - - /// - /// Gets fences to wait for use of a given buffer region. - /// - /// Span to store overlapping fences in - /// Offset of the range - /// Size of the range in bytes - /// Number of fences for the specified region placed in storage - private int GetOverlappingFences(Span storage, int offset, int size) - { - int count = 0; - - for (int i = 0; i < _fences.Length; i++) - { - FenceHolder fence = _fences[i]; - - if (fence != null && _bufferUsageBitmap.OverlapsWith(i, offset, size)) - { - storage[count++] = fence; - } - } - - return count; - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/PersistentFlushBuffer.cs b/src/Ryujinx.Graphics.Metal/PersistentFlushBuffer.cs deleted file mode 100644 index fc79a40a9..000000000 --- a/src/Ryujinx.Graphics.Metal/PersistentFlushBuffer.cs +++ /dev/null @@ -1,100 +0,0 @@ -using Ryujinx.Graphics.GAL; -using SharpMetal.Metal; -using System; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - internal class PersistentFlushBuffer : IDisposable - { - private readonly MetalRenderer _renderer; - - private BufferHolder _flushStorage; - - public PersistentFlushBuffer(MetalRenderer renderer) - { - _renderer = renderer; - } - - private BufferHolder ResizeIfNeeded(int size) - { - BufferHolder flushStorage = _flushStorage; - - if (flushStorage == null || size > _flushStorage.Size) - { - flushStorage?.Dispose(); - - flushStorage = _renderer.BufferManager.Create(size); - _flushStorage = flushStorage; - } - - return flushStorage; - } - - public Span GetBufferData(CommandBufferPool cbp, BufferHolder buffer, int offset, int size) - { - BufferHolder flushStorage = ResizeIfNeeded(size); - Auto srcBuffer; - - using (CommandBufferScoped cbs = cbp.Rent()) - { - srcBuffer = buffer.GetBuffer(); - Auto dstBuffer = flushStorage.GetBuffer(); - - if (srcBuffer.TryIncrementReferenceCount()) - { - BufferHolder.Copy(cbs, srcBuffer, dstBuffer, offset, 0, size, registerSrcUsage: false); - } - else - { - // Source buffer is no longer alive, don't copy anything to flush storage. - srcBuffer = null; - } - } - - flushStorage.WaitForFences(); - srcBuffer?.DecrementReferenceCount(); - return flushStorage.GetDataStorage(0, size); - } - - public Span GetTextureData(CommandBufferPool cbp, Texture view, int size) - { - TextureCreateInfo info = view.Info; - - BufferHolder flushStorage = ResizeIfNeeded(size); - - using (CommandBufferScoped cbs = cbp.Rent()) - { - MTLBuffer buffer = flushStorage.GetBuffer().Get(cbs).Value; - MTLTexture image = view.GetHandle(); - - view.CopyFromOrToBuffer(cbs, buffer, image, size, true, 0, 0, info.GetLayers(), info.Levels, singleSlice: false); - } - - flushStorage.WaitForFences(); - return flushStorage.GetDataStorage(0, size); - } - - public Span GetTextureData(CommandBufferPool cbp, Texture view, int size, int layer, int level) - { - BufferHolder flushStorage = ResizeIfNeeded(size); - - using (CommandBufferScoped cbs = cbp.Rent()) - { - MTLBuffer buffer = flushStorage.GetBuffer().Get(cbs).Value; - MTLTexture image = view.GetHandle(); - - view.CopyFromOrToBuffer(cbs, buffer, image, size, true, layer, level, 1, 1, singleSlice: true); - } - - flushStorage.WaitForFences(); - return flushStorage.GetDataStorage(0, size); - } - - public void Dispose() - { - _flushStorage.Dispose(); - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs deleted file mode 100644 index aebcb5493..000000000 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ /dev/null @@ -1,877 +0,0 @@ -using Ryujinx.Common.Logging; -using Ryujinx.Graphics.GAL; -using Ryujinx.Graphics.Shader; -using SharpMetal.Foundation; -using SharpMetal.Metal; -using SharpMetal.QuartzCore; -using System; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - public enum EncoderType - { - Blit, - Compute, - Render, - None - } - - [SupportedOSPlatform("macos")] - class Pipeline : IPipeline, IEncoderFactory, IDisposable - { - private const ulong MinByteWeightForFlush = 256 * 1024 * 1024; // MiB - - private readonly MTLDevice _device; - private readonly MetalRenderer _renderer; - private EncoderStateManager _encoderStateManager; - private ulong _byteWeight; - - public MTLCommandBuffer CommandBuffer; - - public IndexBufferPattern QuadsToTrisPattern; - public IndexBufferPattern TriFanToTrisPattern; - - internal CommandBufferScoped? PreloadCbs { get; private set; } - internal CommandBufferScoped Cbs { get; private set; } - internal CommandBufferEncoder Encoders => Cbs.Encoders; - internal EncoderType CurrentEncoderType => Encoders.CurrentEncoderType; - - public Pipeline(MTLDevice device, MetalRenderer renderer) - { - _device = device; - _renderer = renderer; - - renderer.CommandBufferPool.Initialize(this); - - CommandBuffer = (Cbs = _renderer.CommandBufferPool.Rent()).CommandBuffer; - } - - internal void InitEncoderStateManager(BufferManager bufferManager) - { - _encoderStateManager = new EncoderStateManager(_device, bufferManager, this); - - QuadsToTrisPattern = new IndexBufferPattern(_renderer, 4, 6, 0, [0, 1, 2, 0, 2, 3], 4, false); - TriFanToTrisPattern = new IndexBufferPattern(_renderer, 3, 3, 2, [int.MinValue, -1, 0], 1, true); - } - - public EncoderState SwapState(EncoderState state, DirtyFlags flags = DirtyFlags.All, bool endRenderPass = true) - { - if (endRenderPass && CurrentEncoderType == EncoderType.Render) - { - EndCurrentPass(); - } - - return _encoderStateManager.SwapState(state, flags); - } - - public PredrawState SavePredrawState() - { - return _encoderStateManager.SavePredrawState(); - } - - public void RestorePredrawState(PredrawState state) - { - _encoderStateManager.RestorePredrawState(state); - } - - public void SetClearLoadAction(bool clear) - { - _encoderStateManager.SetClearLoadAction(clear); - } - - public MTLRenderCommandEncoder GetOrCreateRenderEncoder(bool forDraw = false) - { - // Mark all state as dirty to ensure it is set on the new encoder - if (Cbs.Encoders.CurrentEncoderType != EncoderType.Render) - { - _encoderStateManager.SignalRenderDirty(); - } - - if (forDraw) - { - _encoderStateManager.RenderResourcesPrepass(); - } - - MTLRenderCommandEncoder renderCommandEncoder = Cbs.Encoders.EnsureRenderEncoder(); - - if (forDraw) - { - _encoderStateManager.RebindRenderState(renderCommandEncoder); - } - - return renderCommandEncoder; - } - - public MTLBlitCommandEncoder GetOrCreateBlitEncoder() - { - return Cbs.Encoders.EnsureBlitEncoder(); - } - - public MTLComputeCommandEncoder GetOrCreateComputeEncoder(bool forDispatch = false) - { - // Mark all state as dirty to ensure it is set on the new encoder - if (Cbs.Encoders.CurrentEncoderType != EncoderType.Compute) - { - _encoderStateManager.SignalComputeDirty(); - } - - if (forDispatch) - { - _encoderStateManager.ComputeResourcesPrepass(); - } - - MTLComputeCommandEncoder computeCommandEncoder = Cbs.Encoders.EnsureComputeEncoder(); - - if (forDispatch) - { - _encoderStateManager.RebindComputeState(computeCommandEncoder); - } - - return computeCommandEncoder; - } - - public void EndCurrentPass() - { - Cbs.Encoders.EndCurrentPass(); - } - - public MTLRenderCommandEncoder CreateRenderCommandEncoder() - { - return _encoderStateManager.CreateRenderCommandEncoder(); - } - - public MTLComputeCommandEncoder CreateComputeCommandEncoder() - { - return _encoderStateManager.CreateComputeCommandEncoder(); - } - - public void Present(CAMetalDrawable drawable, Texture src, Extents2D srcRegion, Extents2D dstRegion, bool isLinear) - { - // TODO: Clean this up - TextureCreateInfo textureInfo = new((int)drawable.Texture.Width, (int)drawable.Texture.Height, (int)drawable.Texture.Depth, (int)drawable.Texture.MipmapLevelCount, (int)drawable.Texture.SampleCount, 0, 0, 0, Format.B8G8R8A8Unorm, 0, Target.Texture2D, SwizzleComponent.Red, SwizzleComponent.Green, SwizzleComponent.Blue, SwizzleComponent.Alpha); - Texture dst = new(_device, _renderer, this, textureInfo, drawable.Texture, 0, 0); - - _renderer.HelperShader.BlitColor(Cbs, src, dst, srcRegion, dstRegion, isLinear, true); - - EndCurrentPass(); - - Cbs.CommandBuffer.PresentDrawable(drawable); - - FlushCommandsImpl(); - - // TODO: Auto flush counting - _renderer.SyncManager.GetAndResetWaitTicks(); - - // Cleanup - dst.Dispose(); - } - - public CommandBufferScoped GetPreloadCommandBuffer() - { - PreloadCbs ??= _renderer.CommandBufferPool.Rent(); - - return PreloadCbs.Value; - } - - public void FlushCommandsIfWeightExceeding(IAuto disposedResource, ulong byteWeight) - { - bool usedByCurrentCb = disposedResource.HasCommandBufferDependency(Cbs); - - if (PreloadCbs != null && !usedByCurrentCb) - { - usedByCurrentCb = disposedResource.HasCommandBufferDependency(PreloadCbs.Value); - } - - if (usedByCurrentCb) - { - // Since we can only free memory after the command buffer that uses a given resource was executed, - // keeping the command buffer might cause a high amount of memory to be in use. - // To prevent that, we force submit command buffers if the memory usage by resources - // in use by the current command buffer is above a given limit, and those resources were disposed. - _byteWeight += byteWeight; - - if (_byteWeight >= MinByteWeightForFlush) - { - FlushCommandsImpl(); - } - } - } - - public void FlushCommandsImpl() - { - EndCurrentPass(); - - _byteWeight = 0; - - if (PreloadCbs != null) - { - PreloadCbs.Value.Dispose(); - PreloadCbs = null; - } - - CommandBuffer = (Cbs = _renderer.CommandBufferPool.ReturnAndRent(Cbs)).CommandBuffer; - _renderer.RegisterFlush(); - } - - public void DirtyTextures() - { - _encoderStateManager.DirtyTextures(); - } - - public void DirtyImages() - { - _encoderStateManager.DirtyImages(); - } - - public void Blit( - Texture src, - Texture dst, - Extents2D srcRegion, - Extents2D dstRegion, - bool isDepthOrStencil, - bool linearFilter) - { - if (isDepthOrStencil) - { - _renderer.HelperShader.BlitDepthStencil(Cbs, src, dst, srcRegion, dstRegion); - } - else - { - _renderer.HelperShader.BlitColor(Cbs, src, dst, srcRegion, dstRegion, linearFilter); - } - } - - public void Barrier() - { - switch (CurrentEncoderType) - { - case EncoderType.Render: - { - MTLBarrierScope scope = MTLBarrierScope.Buffers | MTLBarrierScope.Textures | MTLBarrierScope.RenderTargets; - MTLRenderStages stages = MTLRenderStages.RenderStageVertex | MTLRenderStages.RenderStageFragment; - Encoders.RenderEncoder.MemoryBarrier(scope, stages, stages); - break; - } - case EncoderType.Compute: - { - MTLBarrierScope scope = MTLBarrierScope.Buffers | MTLBarrierScope.Textures | MTLBarrierScope.RenderTargets; - Encoders.ComputeEncoder.MemoryBarrier(scope); - break; - } - } - } - - public void ClearBuffer(BufferHandle destination, int offset, int size, uint value) - { - MTLBlitCommandEncoder blitCommandEncoder = GetOrCreateBlitEncoder(); - - MTLBuffer mtlBuffer = _renderer.BufferManager.GetBuffer(destination, offset, size, true).Get(Cbs, offset, size, true).Value; - - // Might need a closer look, range's count, lower, and upper bound - // must be a multiple of 4 - blitCommandEncoder.FillBuffer(mtlBuffer, - new NSRange - { - location = (ulong)offset, - length = (ulong)size - }, - (byte)value); - } - - public void ClearRenderTargetColor(int index, int layer, int layerCount, uint componentMask, ColorF color) - { - float[] colors = [color.Red, color.Green, color.Blue, color.Alpha]; - Texture dst = _encoderStateManager.RenderTargets[index]; - - // TODO: Remove workaround for Wonder which has an invalid texture due to unsupported format - if (dst == null) - { - Logger.Warning?.PrintMsg(LogClass.Gpu, "Attempted to clear invalid render target!"); - return; - } - - _renderer.HelperShader.ClearColor(index, colors, componentMask, dst.Width, dst.Height, dst.Info.Format); - } - - public void ClearRenderTargetDepthStencil(int layer, int layerCount, float depthValue, bool depthMask, int stencilValue, int stencilMask) - { - Texture depthStencil = _encoderStateManager.DepthStencil; - - if (depthStencil == null) - { - return; - } - - _renderer.HelperShader.ClearDepthStencil(depthValue, depthMask, stencilValue, stencilMask, depthStencil.Width, depthStencil.Height); - } - - public void CommandBufferBarrier() - { - Barrier(); - } - - public void CopyBuffer(BufferHandle src, BufferHandle dst, int srcOffset, int dstOffset, int size) - { - Auto srcBuffer = _renderer.BufferManager.GetBuffer(src, srcOffset, size, false); - Auto dstBuffer = _renderer.BufferManager.GetBuffer(dst, dstOffset, size, true); - - BufferHolder.Copy(Cbs, srcBuffer, dstBuffer, srcOffset, dstOffset, size); - } - - public void PushDebugGroup(string name) - { - MTLCommandEncoder? encoder = Encoders.CurrentEncoder; - NSString debugGroupName = StringHelper.NSString(name); - - if (encoder == null) - { - return; - } - - switch (Encoders.CurrentEncoderType) - { - case EncoderType.Render: - encoder.Value.PushDebugGroup(debugGroupName); - break; - case EncoderType.Blit: - encoder.Value.PushDebugGroup(debugGroupName); - break; - case EncoderType.Compute: - encoder.Value.PushDebugGroup(debugGroupName); - break; - } - } - - public void PopDebugGroup() - { - MTLCommandEncoder? encoder = Encoders.CurrentEncoder; - - if (encoder == null) - { - return; - } - - switch (Encoders.CurrentEncoderType) - { - case EncoderType.Render: - encoder.Value.PopDebugGroup(); - break; - case EncoderType.Blit: - encoder.Value.PopDebugGroup(); - break; - case EncoderType.Compute: - encoder.Value.PopDebugGroup(); - break; - } - } - - public void DispatchCompute(int groupsX, int groupsY, int groupsZ) - { - DispatchCompute(groupsX, groupsY, groupsZ, String.Empty); - } - - public void DispatchCompute(int groupsX, int groupsY, int groupsZ, string debugGroupName) - { - MTLComputeCommandEncoder computeCommandEncoder = GetOrCreateComputeEncoder(true); - - ComputeSize localSize = _encoderStateManager.ComputeLocalSize; - - if (debugGroupName != String.Empty) - { - PushDebugGroup(debugGroupName); - } - - computeCommandEncoder.DispatchThreadgroups( - new MTLSize { width = (ulong)groupsX, height = (ulong)groupsY, depth = (ulong)groupsZ }, - new MTLSize { width = (ulong)localSize.X, height = (ulong)localSize.Y, depth = (ulong)localSize.Z }); - - if (debugGroupName != String.Empty) - { - PopDebugGroup(); - } - } - - public void Draw(int vertexCount, int instanceCount, int firstVertex, int firstInstance) - { - Draw(vertexCount, instanceCount, firstVertex, firstInstance, String.Empty); - } - - public void Draw(int vertexCount, int instanceCount, int firstVertex, int firstInstance, string debugGroupName) - { - if (vertexCount == 0) - { - return; - } - - MTLPrimitiveType primitiveType = TopologyRemap(_encoderStateManager.Topology).Convert(); - - if (TopologyUnsupported(_encoderStateManager.Topology)) - { - IndexBufferPattern pattern = GetIndexBufferPattern(); - - BufferHandle handle = pattern.GetRepeatingBuffer(vertexCount, out int indexCount); - Auto buffer = _renderer.BufferManager.GetBuffer(handle, false); - MTLBuffer mtlBuffer = buffer.Get(Cbs, 0, indexCount * sizeof(int)).Value; - - MTLRenderCommandEncoder renderCommandEncoder = GetOrCreateRenderEncoder(true); - - renderCommandEncoder.DrawIndexedPrimitives( - primitiveType, - (ulong)indexCount, - MTLIndexType.UInt32, - mtlBuffer, - 0); - } - else - { - MTLRenderCommandEncoder renderCommandEncoder = GetOrCreateRenderEncoder(true); - - if (debugGroupName != String.Empty) - { - PushDebugGroup(debugGroupName); - } - - renderCommandEncoder.DrawPrimitives( - primitiveType, - (ulong)firstVertex, - (ulong)vertexCount, - (ulong)instanceCount, - (ulong)firstInstance); - - if (debugGroupName != String.Empty) - { - PopDebugGroup(); - } - } - } - - private IndexBufferPattern GetIndexBufferPattern() - { - return _encoderStateManager.Topology switch - { - PrimitiveTopology.Quads => QuadsToTrisPattern, - PrimitiveTopology.TriangleFan or PrimitiveTopology.Polygon => TriFanToTrisPattern, - _ => throw new NotSupportedException($"Unsupported topology: {_encoderStateManager.Topology}"), - }; - } - - private PrimitiveTopology TopologyRemap(PrimitiveTopology topology) - { - return topology switch - { - PrimitiveTopology.Quads => PrimitiveTopology.Triangles, - PrimitiveTopology.QuadStrip => PrimitiveTopology.TriangleStrip, - PrimitiveTopology.TriangleFan or PrimitiveTopology.Polygon => PrimitiveTopology.Triangles, - _ => topology, - }; - } - - private bool TopologyUnsupported(PrimitiveTopology topology) - { - return topology switch - { - PrimitiveTopology.Quads or PrimitiveTopology.TriangleFan or PrimitiveTopology.Polygon => true, - _ => false, - }; - } - - public void DrawIndexed(int indexCount, int instanceCount, int firstIndex, int firstVertex, int firstInstance) - { - if (indexCount == 0) - { - return; - } - - MTLBuffer mtlBuffer; - int offset; - MTLIndexType type; - int finalIndexCount = indexCount; - - MTLPrimitiveType primitiveType = TopologyRemap(_encoderStateManager.Topology).Convert(); - - if (TopologyUnsupported(_encoderStateManager.Topology)) - { - IndexBufferPattern pattern = GetIndexBufferPattern(); - int convertedCount = pattern.GetConvertedCount(indexCount); - - finalIndexCount = convertedCount; - - (mtlBuffer, offset, type) = _encoderStateManager.IndexBuffer.GetConvertedIndexBuffer(_renderer, Cbs, firstIndex, indexCount, convertedCount, pattern); - } - else - { - (mtlBuffer, offset, type) = _encoderStateManager.IndexBuffer.GetIndexBuffer(_renderer, Cbs); - } - - if (mtlBuffer.NativePtr != IntPtr.Zero) - { - MTLRenderCommandEncoder renderCommandEncoder = GetOrCreateRenderEncoder(true); - - renderCommandEncoder.DrawIndexedPrimitives( - primitiveType, - (ulong)finalIndexCount, - type, - mtlBuffer, - (ulong)offset, - (ulong)instanceCount, - firstVertex, - (ulong)firstInstance); - } - } - - public void DrawIndexedIndirect(BufferRange indirectBuffer) - { - DrawIndexedIndirectOffset(indirectBuffer); - } - - public void DrawIndexedIndirectOffset(BufferRange indirectBuffer, int offset = 0) - { - // TODO: Reindex unsupported topologies - if (TopologyUnsupported(_encoderStateManager.Topology)) - { - Logger.Warning?.Print(LogClass.Gpu, $"Drawing indexed with unsupported topology: {_encoderStateManager.Topology}"); - } - - MTLBuffer buffer = _renderer.BufferManager - .GetBuffer(indirectBuffer.Handle, indirectBuffer.Offset, indirectBuffer.Size, false) - .Get(Cbs, indirectBuffer.Offset, indirectBuffer.Size).Value; - - MTLPrimitiveType primitiveType = TopologyRemap(_encoderStateManager.Topology).Convert(); - - (MTLBuffer indexBuffer, int indexOffset, MTLIndexType type) = _encoderStateManager.IndexBuffer.GetIndexBuffer(_renderer, Cbs); - - if (indexBuffer.NativePtr != IntPtr.Zero && buffer.NativePtr != IntPtr.Zero) - { - MTLRenderCommandEncoder renderCommandEncoder = GetOrCreateRenderEncoder(true); - - renderCommandEncoder.DrawIndexedPrimitives( - primitiveType, - type, - indexBuffer, - (ulong)indexOffset, - buffer, - (ulong)(indirectBuffer.Offset + offset)); - } - } - - public void DrawIndexedIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride) - { - for (int i = 0; i < maxDrawCount; i++) - { - DrawIndexedIndirectOffset(indirectBuffer, stride * i); - } - } - - public void DrawIndirect(BufferRange indirectBuffer) - { - DrawIndirectOffset(indirectBuffer); - } - - public void DrawIndirectOffset(BufferRange indirectBuffer, int offset = 0) - { - if (TopologyUnsupported(_encoderStateManager.Topology)) - { - // TODO: Reindex unsupported topologies - Logger.Warning?.Print(LogClass.Gpu, $"Drawing indirect with unsupported topology: {_encoderStateManager.Topology}"); - } - - MTLBuffer buffer = _renderer.BufferManager - .GetBuffer(indirectBuffer.Handle, indirectBuffer.Offset, indirectBuffer.Size, false) - .Get(Cbs, indirectBuffer.Offset, indirectBuffer.Size).Value; - - MTLPrimitiveType primitiveType = TopologyRemap(_encoderStateManager.Topology).Convert(); - MTLRenderCommandEncoder renderCommandEncoder = GetOrCreateRenderEncoder(true); - - renderCommandEncoder.DrawPrimitives( - primitiveType, - buffer, - (ulong)(indirectBuffer.Offset + offset)); - } - - public void DrawIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride) - { - for (int i = 0; i < maxDrawCount; i++) - { - DrawIndirectOffset(indirectBuffer, stride * i); - } - } - - public void DrawTexture(ITexture texture, ISampler sampler, Extents2DF srcRegion, Extents2DF dstRegion) - { - _renderer.HelperShader.DrawTexture(texture, sampler, srcRegion, dstRegion); - } - - public void SetAlphaTest(bool enable, float reference, CompareOp op) - { - // This is currently handled using shader specialization, as Metal does not support alpha test. - // In the future, we may want to use this to write the reference value into the support buffer, - // to avoid creating one version of the shader per reference value used. - } - - public void SetBlendState(AdvancedBlendDescriptor blend) - { - // Metal does not support advanced blend. - } - - public void SetBlendState(int index, BlendDescriptor blend) - { - _encoderStateManager.UpdateBlendDescriptors(index, blend); - } - - public void SetDepthBias(PolygonModeMask enables, float factor, float units, float clamp) - { - if (enables == 0) - { - _encoderStateManager.UpdateDepthBias(0, 0, 0); - } - else - { - _encoderStateManager.UpdateDepthBias(units, factor, clamp); - } - } - - public void SetDepthClamp(bool clamp) - { - _encoderStateManager.UpdateDepthClamp(clamp); - } - - public void SetDepthMode(DepthMode mode) - { - // Metal does not support depth clip control. - } - - public void SetDepthTest(DepthTestDescriptor depthTest) - { - _encoderStateManager.UpdateDepthState(depthTest); - } - - public void SetFaceCulling(bool enable, Face face) - { - _encoderStateManager.UpdateCullMode(enable, face); - } - - public void SetFrontFace(FrontFace frontFace) - { - _encoderStateManager.UpdateFrontFace(frontFace); - } - - public void SetIndexBuffer(BufferRange buffer, IndexType type) - { - _encoderStateManager.UpdateIndexBuffer(buffer, type); - } - - public void SetImage(ShaderStage stage, int binding, ITexture image) - { - if (image is TextureBase img) - { - _encoderStateManager.UpdateImage(stage, binding, img); - } - } - - public void SetImageArray(ShaderStage stage, int binding, IImageArray array) - { - if (array is ImageArray imageArray) - { - _encoderStateManager.UpdateImageArray(stage, binding, imageArray); - } - } - - public void SetImageArraySeparate(ShaderStage stage, int setIndex, IImageArray array) - { - if (array is ImageArray imageArray) - { - _encoderStateManager.UpdateImageArraySeparate(stage, setIndex, imageArray); - } - } - - public void SetLineParameters(float width, bool smooth) - { - // Metal does not support wide-lines. - } - - public void SetLogicOpState(bool enable, LogicalOp op) - { - _encoderStateManager.UpdateLogicOpState(enable, op); - } - - public void SetMultisampleState(MultisampleDescriptor multisample) - { - _encoderStateManager.UpdateMultisampleState(multisample); - } - - public void SetPatchParameters(int vertices, ReadOnlySpan defaultOuterLevel, ReadOnlySpan defaultInnerLevel) - { - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); - } - - public void SetPointParameters(float size, bool isProgramPointSize, bool enablePointSprite, Origin origin) - { - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); - } - - public void SetPolygonMode(PolygonMode frontMode, PolygonMode backMode) - { - // Metal does not support polygon mode. - } - - public void SetPrimitiveRestart(bool enable, int index) - { - // Always active for LineStrip and TriangleStrip - // https://github.com/gpuweb/gpuweb/issues/1220#issuecomment-732483263 - // https://developer.apple.com/documentation/metal/mtlrendercommandencoder/1515520-drawindexedprimitives - // https://stackoverflow.com/questions/70813665/how-to-render-multiple-trianglestrips-using-metal - - // Emulating disabling this is very difficult. It's unlikely for an index buffer to use the largest possible index, - // so it's fine nearly all of the time. - } - - public void SetPrimitiveTopology(PrimitiveTopology topology) - { - _encoderStateManager.UpdatePrimitiveTopology(topology); - } - - public void SetProgram(IProgram program) - { - _encoderStateManager.UpdateProgram(program); - } - - public void SetRasterizerDiscard(bool discard) - { - _encoderStateManager.UpdateRasterizerDiscard(discard); - } - - public void SetRenderTargetColorMasks(ReadOnlySpan componentMask) - { - _encoderStateManager.UpdateRenderTargetColorMasks(componentMask); - } - - public void SetRenderTargets(ITexture[] colors, ITexture depthStencil) - { - _encoderStateManager.UpdateRenderTargets(colors, depthStencil); - } - - public void SetScissors(ReadOnlySpan> regions) - { - _encoderStateManager.UpdateScissors(regions); - } - - public void SetStencilTest(StencilTestDescriptor stencilTest) - { - _encoderStateManager.UpdateStencilState(stencilTest); - } - - public void SetUniformBuffers(ReadOnlySpan buffers) - { - _encoderStateManager.UpdateUniformBuffers(buffers); - } - - public void SetStorageBuffers(ReadOnlySpan buffers) - { - _encoderStateManager.UpdateStorageBuffers(buffers); - } - - internal void SetStorageBuffers(int first, ReadOnlySpan> buffers) - { - _encoderStateManager.UpdateStorageBuffers(first, buffers); - } - - public void SetTextureAndSampler(ShaderStage stage, int binding, ITexture texture, ISampler sampler) - { - if (texture is TextureBase tex) - { - if (sampler == null || sampler is SamplerHolder) - { - _encoderStateManager.UpdateTextureAndSampler(stage, binding, tex, (SamplerHolder)sampler); - } - } - } - - public void SetTextureArray(ShaderStage stage, int binding, ITextureArray array) - { - if (array is TextureArray textureArray) - { - _encoderStateManager.UpdateTextureArray(stage, binding, textureArray); - } - } - - public void SetTextureArraySeparate(ShaderStage stage, int setIndex, ITextureArray array) - { - if (array is TextureArray textureArray) - { - _encoderStateManager.UpdateTextureArraySeparate(stage, setIndex, textureArray); - } - } - - public void SetUserClipDistance(int index, bool enableClip) - { - // TODO. Same as Vulkan - } - - public void SetVertexAttribs(ReadOnlySpan vertexAttribs) - { - _encoderStateManager.UpdateVertexAttribs(vertexAttribs); - } - - public void SetVertexBuffers(ReadOnlySpan vertexBuffers) - { - _encoderStateManager.UpdateVertexBuffers(vertexBuffers); - } - - public void SetViewports(ReadOnlySpan viewports) - { - _encoderStateManager.UpdateViewports(viewports); - } - - public void TextureBarrier() - { - if (CurrentEncoderType == EncoderType.Render) - { - Encoders.RenderEncoder.MemoryBarrier(MTLBarrierScope.Textures, MTLRenderStages.RenderStageFragment, MTLRenderStages.RenderStageFragment); - } - } - - public void TextureBarrierTiled() - { - TextureBarrier(); - } - - public bool TryHostConditionalRendering(ICounterEvent value, ulong compare, bool isEqual) - { - // TODO: Implementable via indirect draw commands - return false; - } - - public bool TryHostConditionalRendering(ICounterEvent value, ICounterEvent compare, bool isEqual) - { - // TODO: Implementable via indirect draw commands - return false; - } - - public void EndHostConditionalRendering() - { - // TODO: Implementable via indirect draw commands - } - - public void BeginTransformFeedback(PrimitiveTopology topology) - { - // Metal does not support transform feedback. - } - - public void EndTransformFeedback() - { - // Metal does not support transform feedback. - } - - public void SetTransformFeedbackBuffers(ReadOnlySpan buffers) - { - // Metal does not support transform feedback. - } - - public void Dispose() - { - EndCurrentPass(); - _encoderStateManager.Dispose(); - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/Program.cs b/src/Ryujinx.Graphics.Metal/Program.cs deleted file mode 100644 index 721ee56a7..000000000 --- a/src/Ryujinx.Graphics.Metal/Program.cs +++ /dev/null @@ -1,286 +0,0 @@ -using Ryujinx.Common.Logging; -using Ryujinx.Graphics.GAL; -using Ryujinx.Graphics.Shader; -using SharpMetal.Foundation; -using SharpMetal.Metal; -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Runtime.InteropServices; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - class Program : IProgram - { - private ProgramLinkStatus _status; - private readonly ShaderSource[] _shaders; - private readonly GCHandle[] _handles; - private int _successCount; - - private readonly MetalRenderer _renderer; - - public MTLFunction VertexFunction; - public MTLFunction FragmentFunction; - public MTLFunction ComputeFunction; - public ComputeSize ComputeLocalSize { get; } - - private HashTableSlim _graphicsPipelineCache; - private MTLComputePipelineState? _computePipelineCache; - private bool _firstBackgroundUse; - - public ResourceBindingSegment[][] BindingSegments { get; } - // Argument buffer sizes for Vertex or Compute stages - public int[] ArgumentBufferSizes { get; } - // Argument buffer sizes for Fragment stage - public int[] FragArgumentBufferSizes { get; } - - public Program( - MetalRenderer renderer, - MTLDevice device, - ShaderSource[] shaders, - ResourceLayout resourceLayout, - ComputeSize computeLocalSize = default) - { - _renderer = renderer; - renderer.Programs.Add(this); - - ComputeLocalSize = computeLocalSize; - _shaders = shaders; - _handles = new GCHandle[_shaders.Length]; - - _status = ProgramLinkStatus.Incomplete; - - for (int i = 0; i < _shaders.Length; i++) - { - ShaderSource shader = _shaders[i]; - - using MTLCompileOptions compileOptions = new() - { - PreserveInvariance = true, - LanguageVersion = MTLLanguageVersion.Version31, - }; - int index = i; - - _handles[i] = device.NewLibrary(StringHelper.NSString(shader.Code), compileOptions, (library, error) => CompilationResultHandler(library, error, index)); - } - - (BindingSegments, ArgumentBufferSizes, FragArgumentBufferSizes) = BuildBindingSegments(resourceLayout.SetUsages); - } - - public void CompilationResultHandler(MTLLibrary library, NSError error, int index) - { - ShaderSource shader = _shaders[index]; - - if (_handles[index].IsAllocated) - { - _handles[index].Free(); - } - - if (error != IntPtr.Zero) - { - Logger.Warning?.PrintMsg(LogClass.Gpu, shader.Code); - Logger.Warning?.Print(LogClass.Gpu, $"{shader.Stage} shader linking failed: \n{StringHelper.String(error.LocalizedDescription)}"); - _status = ProgramLinkStatus.Failure; - return; - } - - switch (shader.Stage) - { - case ShaderStage.Compute: - ComputeFunction = library.NewFunction(StringHelper.NSString("kernelMain")); - break; - case ShaderStage.Vertex: - VertexFunction = library.NewFunction(StringHelper.NSString("vertexMain")); - break; - case ShaderStage.Fragment: - FragmentFunction = library.NewFunction(StringHelper.NSString("fragmentMain")); - break; - default: - Logger.Warning?.Print(LogClass.Gpu, $"Cannot handle stage {shader.Stage}!"); - break; - } - - _successCount++; - - if (_successCount >= _shaders.Length && _status != ProgramLinkStatus.Failure) - { - _status = ProgramLinkStatus.Success; - } - } - - private static (ResourceBindingSegment[][], int[], int[]) BuildBindingSegments(ReadOnlyCollection setUsages) - { - ResourceBindingSegment[][] segments = new ResourceBindingSegment[setUsages.Count][]; - int[] argBufferSizes = new int[setUsages.Count]; - int[] fragArgBufferSizes = new int[setUsages.Count]; - - for (int setIndex = 0; setIndex < setUsages.Count; setIndex++) - { - List currentSegments = []; - - ResourceUsage currentUsage = default; - int currentCount = 0; - - for (int index = 0; index < setUsages[setIndex].Usages.Count; index++) - { - ResourceUsage usage = setUsages[setIndex].Usages[index]; - - if (currentUsage.Binding + currentCount != usage.Binding || - currentUsage.Type != usage.Type || - currentUsage.Stages != usage.Stages || - currentUsage.ArrayLength > 1 || - usage.ArrayLength > 1) - { - if (currentCount != 0) - { - currentSegments.Add(new ResourceBindingSegment( - currentUsage.Binding, - currentCount, - currentUsage.Type, - currentUsage.Stages, - currentUsage.ArrayLength > 1)); - - int size = currentCount * ResourcePointerSize(currentUsage.Type); - if (currentUsage.Stages.HasFlag(ResourceStages.Fragment)) - { - fragArgBufferSizes[setIndex] += size; - } - - if (currentUsage.Stages.HasFlag(ResourceStages.Vertex) || - currentUsage.Stages.HasFlag(ResourceStages.Compute)) - { - argBufferSizes[setIndex] += size; - } - } - - currentUsage = usage; - currentCount = usage.ArrayLength; - } - else - { - currentCount++; - } - } - - if (currentCount != 0) - { - currentSegments.Add(new ResourceBindingSegment( - currentUsage.Binding, - currentCount, - currentUsage.Type, - currentUsage.Stages, - currentUsage.ArrayLength > 1)); - - int size = currentCount * ResourcePointerSize(currentUsage.Type); - if (currentUsage.Stages.HasFlag(ResourceStages.Fragment)) - { - fragArgBufferSizes[setIndex] += size; - } - - if (currentUsage.Stages.HasFlag(ResourceStages.Vertex) || - currentUsage.Stages.HasFlag(ResourceStages.Compute)) - { - argBufferSizes[setIndex] += size; - } - } - - segments[setIndex] = currentSegments.ToArray(); - } - - return (segments, argBufferSizes, fragArgBufferSizes); - } - - private static int ResourcePointerSize(ResourceType type) - { - return (type == ResourceType.TextureAndSampler ? 2 : 1); - } - - public ProgramLinkStatus CheckProgramLink(bool blocking) - { - if (blocking) - { - while (_status == ProgramLinkStatus.Incomplete) - { } - - return _status; - } - - return _status; - } - - public byte[] GetBinary() - { - return []; - } - - public void AddGraphicsPipeline(ref PipelineUid key, MTLRenderPipelineState pipeline) - { - (_graphicsPipelineCache ??= new()).Add(ref key, pipeline); - } - - public void AddComputePipeline(MTLComputePipelineState pipeline) - { - _computePipelineCache = pipeline; - } - - public bool TryGetGraphicsPipeline(ref PipelineUid key, out MTLRenderPipelineState pipeline) - { - if (_graphicsPipelineCache == null) - { - pipeline = default; - return false; - } - - if (!_graphicsPipelineCache.TryGetValue(ref key, out pipeline)) - { - if (_firstBackgroundUse) - { - Logger.Warning?.Print(LogClass.Gpu, "Background pipeline compile missed on draw - incorrect pipeline state?"); - _firstBackgroundUse = false; - } - - return false; - } - - _firstBackgroundUse = false; - - return true; - } - - public bool TryGetComputePipeline(out MTLComputePipelineState pipeline) - { - if (_computePipelineCache.HasValue) - { - pipeline = _computePipelineCache.Value; - return true; - } - - pipeline = default; - return false; - } - - public void Dispose() - { - if (!_renderer.Programs.Remove(this)) - { - return; - } - - if (_graphicsPipelineCache != null) - { - foreach (MTLRenderPipelineState pipeline in _graphicsPipelineCache.Values) - { - pipeline.Dispose(); - } - } - - _computePipelineCache?.Dispose(); - - VertexFunction.Dispose(); - FragmentFunction.Dispose(); - ComputeFunction.Dispose(); - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/ResourceBindingSegment.cs b/src/Ryujinx.Graphics.Metal/ResourceBindingSegment.cs deleted file mode 100644 index 8e6d88c4b..000000000 --- a/src/Ryujinx.Graphics.Metal/ResourceBindingSegment.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Ryujinx.Graphics.GAL; - -namespace Ryujinx.Graphics.Metal -{ - readonly struct ResourceBindingSegment - { - public readonly int Binding; - public readonly int Count; - public readonly ResourceType Type; - public readonly ResourceStages Stages; - public readonly bool IsArray; - - public ResourceBindingSegment(int binding, int count, ResourceType type, ResourceStages stages, bool isArray) - { - Binding = binding; - Count = count; - Type = type; - Stages = stages; - IsArray = isArray; - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/ResourceLayoutBuilder.cs b/src/Ryujinx.Graphics.Metal/ResourceLayoutBuilder.cs deleted file mode 100644 index 623f91612..000000000 --- a/src/Ryujinx.Graphics.Metal/ResourceLayoutBuilder.cs +++ /dev/null @@ -1,59 +0,0 @@ -using Ryujinx.Graphics.GAL; -using System; -using System.Collections.Generic; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - class ResourceLayoutBuilder - { - private const int TotalSets = MetalRenderer.TotalSets; - - private readonly List[] _resourceDescriptors; - private readonly List[] _resourceUsages; - - public ResourceLayoutBuilder() - { - _resourceDescriptors = new List[TotalSets]; - _resourceUsages = new List[TotalSets]; - - for (int index = 0; index < TotalSets; index++) - { - _resourceDescriptors[index] = []; - _resourceUsages[index] = []; - } - } - - public ResourceLayoutBuilder Add(ResourceStages stages, ResourceType type, int binding, bool write = false) - { - uint setIndex = type switch - { - ResourceType.UniformBuffer => Constants.ConstantBuffersSetIndex, - ResourceType.StorageBuffer => Constants.StorageBuffersSetIndex, - ResourceType.TextureAndSampler or ResourceType.BufferTexture => Constants.TexturesSetIndex, - ResourceType.Image or ResourceType.BufferImage => Constants.ImagesSetIndex, - _ => throw new ArgumentException($"Invalid resource type \"{type}\"."), - }; - - _resourceDescriptors[setIndex].Add(new ResourceDescriptor(binding, 1, type, stages)); - _resourceUsages[setIndex].Add(new ResourceUsage(binding, 1, type, stages, write)); - - return this; - } - - public ResourceLayout Build() - { - ResourceDescriptorCollection[] descriptors = new ResourceDescriptorCollection[TotalSets]; - ResourceUsageCollection[] usages = new ResourceUsageCollection[TotalSets]; - - for (int index = 0; index < TotalSets; index++) - { - descriptors[index] = new ResourceDescriptorCollection(_resourceDescriptors[index].ToArray().AsReadOnly()); - usages[index] = new ResourceUsageCollection(_resourceUsages[index].ToArray().AsReadOnly()); - } - - return new ResourceLayout(descriptors.AsReadOnly(), usages.AsReadOnly()); - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj b/src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj deleted file mode 100644 index 364aa5a8b..000000000 --- a/src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj +++ /dev/null @@ -1,27 +0,0 @@ - - - - true - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Ryujinx.Graphics.Metal/SamplerHolder.cs b/src/Ryujinx.Graphics.Metal/SamplerHolder.cs deleted file mode 100644 index a448b26fe..000000000 --- a/src/Ryujinx.Graphics.Metal/SamplerHolder.cs +++ /dev/null @@ -1,90 +0,0 @@ -using Ryujinx.Graphics.GAL; -using SharpMetal.Metal; -using System; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - class SamplerHolder : ISampler - { - private readonly MetalRenderer _renderer; - private readonly Auto _sampler; - - public SamplerHolder(MetalRenderer renderer, MTLDevice device, SamplerCreateInfo info) - { - _renderer = renderer; - - renderer.Samplers.Add(this); - - (MTLSamplerMinMagFilter minFilter, MTLSamplerMipFilter mipFilter) = info.MinFilter.Convert(); - - MTLSamplerBorderColor borderColor = GetConstrainedBorderColor(info.BorderColor, out _); - - using MTLSamplerDescriptor descriptor = new() - { - BorderColor = borderColor, - MinFilter = minFilter, - MagFilter = info.MagFilter.Convert(), - MipFilter = mipFilter, - CompareFunction = info.CompareOp.Convert(), - LodMinClamp = info.MinLod, - LodMaxClamp = info.MaxLod, - LodAverage = false, - MaxAnisotropy = Math.Max((uint)info.MaxAnisotropy, 1), - SAddressMode = info.AddressU.Convert(), - TAddressMode = info.AddressV.Convert(), - RAddressMode = info.AddressP.Convert(), - SupportArgumentBuffers = true - }; - - MTLSamplerState sampler = device.NewSamplerState(descriptor); - - _sampler = new Auto(new DisposableSampler(sampler)); - } - - private static MTLSamplerBorderColor GetConstrainedBorderColor(ColorF arbitraryBorderColor, out bool cantConstrain) - { - float r = arbitraryBorderColor.Red; - float g = arbitraryBorderColor.Green; - float b = arbitraryBorderColor.Blue; - float a = arbitraryBorderColor.Alpha; - - if (r == 0f && g == 0f && b == 0f) - { - if (a == 1f) - { - cantConstrain = false; - return MTLSamplerBorderColor.OpaqueBlack; - } - - if (a == 0f) - { - cantConstrain = false; - return MTLSamplerBorderColor.TransparentBlack; - } - } - else if (r == 1f && g == 1f && b == 1f && a == 1f) - { - cantConstrain = false; - return MTLSamplerBorderColor.OpaqueWhite; - } - - cantConstrain = true; - return MTLSamplerBorderColor.OpaqueBlack; - } - - public Auto GetSampler() - { - return _sampler; - } - - public void Dispose() - { - if (_renderer.Samplers.Remove(this)) - { - _sampler.Dispose(); - } - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/Shaders/Blit.metal b/src/Ryujinx.Graphics.Metal/Shaders/Blit.metal deleted file mode 100644 index 887878499..000000000 --- a/src/Ryujinx.Graphics.Metal/Shaders/Blit.metal +++ /dev/null @@ -1,43 +0,0 @@ -#include - -using namespace metal; - -struct CopyVertexOut { - float4 position [[position]]; - float2 uv; -}; - -struct TexCoords { - float data[4]; -}; - -struct ConstantBuffers { - constant TexCoords* tex_coord; -}; - -struct Textures -{ - texture2d texture; - sampler sampler; -}; - -vertex CopyVertexOut vertexMain(uint vid [[vertex_id]], - constant ConstantBuffers &constant_buffers [[buffer(CONSTANT_BUFFERS_INDEX)]]) { - CopyVertexOut out; - - int low = vid & 1; - int high = vid >> 1; - out.uv.x = constant_buffers.tex_coord->data[low]; - out.uv.y = constant_buffers.tex_coord->data[2 + high]; - out.position.x = (float(low) - 0.5f) * 2.0f; - out.position.y = (float(high) - 0.5f) * 2.0f; - out.position.z = 0.0f; - out.position.w = 1.0f; - - return out; -} - -fragment FORMAT4 fragmentMain(CopyVertexOut in [[stage_in]], - constant Textures &textures [[buffer(TEXTURES_INDEX)]]) { - return textures.texture.sample(textures.sampler, in.uv); -} diff --git a/src/Ryujinx.Graphics.Metal/Shaders/BlitMs.metal b/src/Ryujinx.Graphics.Metal/Shaders/BlitMs.metal deleted file mode 100644 index 1077b6cea..000000000 --- a/src/Ryujinx.Graphics.Metal/Shaders/BlitMs.metal +++ /dev/null @@ -1,45 +0,0 @@ -#include - -using namespace metal; - -struct CopyVertexOut { - float4 position [[position]]; - float2 uv; -}; - -struct TexCoords { - float data[4]; -}; - -struct ConstantBuffers { - constant TexCoords* tex_coord; -}; - -struct Textures -{ - texture2d_ms texture; -}; - -vertex CopyVertexOut vertexMain(uint vid [[vertex_id]], - constant ConstantBuffers &constant_buffers [[buffer(CONSTANT_BUFFERS_INDEX)]]) { - CopyVertexOut out; - - int low = vid & 1; - int high = vid >> 1; - out.uv.x = constant_buffers.tex_coord->data[low]; - out.uv.y = constant_buffers.tex_coord->data[2 + high]; - out.position.x = (float(low) - 0.5f) * 2.0f; - out.position.y = (float(high) - 0.5f) * 2.0f; - out.position.z = 0.0f; - out.position.w = 1.0f; - - return out; -} - -fragment FORMAT4 fragmentMain(CopyVertexOut in [[stage_in]], - constant Textures &textures [[buffer(TEXTURES_INDEX)]], - uint sample_id [[sample_id]]) { - uint2 tex_size = uint2(textures.texture.get_width(), textures.texture.get_height()); - uint2 tex_coord = uint2(in.uv * float2(tex_size)); - return textures.texture.read(tex_coord, sample_id); -} diff --git a/src/Ryujinx.Graphics.Metal/Shaders/ChangeBufferStride.metal b/src/Ryujinx.Graphics.Metal/Shaders/ChangeBufferStride.metal deleted file mode 100644 index 1a7d2c574..000000000 --- a/src/Ryujinx.Graphics.Metal/Shaders/ChangeBufferStride.metal +++ /dev/null @@ -1,72 +0,0 @@ -#include - -using namespace metal; - -struct StrideArguments { - int4 data; -}; - -struct InData { - uint8_t data[1]; -}; - -struct OutData { - uint8_t data[1]; -}; - -struct ConstantBuffers { - constant StrideArguments* stride_arguments; -}; - -struct StorageBuffers { - device InData* in_data; - device OutData* out_data; -}; - -kernel void kernelMain(constant ConstantBuffers &constant_buffers [[buffer(CONSTANT_BUFFERS_INDEX)]], - device StorageBuffers &storage_buffers [[buffer(STORAGE_BUFFERS_INDEX)]], - uint3 thread_position_in_grid [[thread_position_in_grid]], - uint3 threads_per_threadgroup [[threads_per_threadgroup]], - uint3 threadgroups_per_grid [[threadgroups_per_grid]]) -{ - // Determine what slice of the stride copies this invocation will perform. - - int sourceStride = constant_buffers.stride_arguments->data.x; - int targetStride = constant_buffers.stride_arguments->data.y; - int bufferSize = constant_buffers.stride_arguments->data.z; - int sourceOffset = constant_buffers.stride_arguments->data.w; - - int strideRemainder = targetStride - sourceStride; - int invocations = int(threads_per_threadgroup.x * threadgroups_per_grid.x); - - int copiesRequired = bufferSize / sourceStride; - - // Find the copies that this invocation should perform. - - // - Copies that all invocations perform. - int allInvocationCopies = copiesRequired / invocations; - - // - Extra remainder copy that this invocation performs. - int index = int(thread_position_in_grid.x); - int extra = (index < (copiesRequired % invocations)) ? 1 : 0; - - int copyCount = allInvocationCopies + extra; - - // Finally, get the starting offset. Make sure to count extra copies. - - int startCopy = allInvocationCopies * index + min(copiesRequired % invocations, index); - - int srcOffset = sourceOffset + startCopy * sourceStride; - int dstOffset = startCopy * targetStride; - - // Perform the copies for this region - for (int i = 0; i < copyCount; i++) { - for (int j = 0; j < sourceStride; j++) { - storage_buffers.out_data->data[dstOffset++] = storage_buffers.in_data->data[srcOffset++]; - } - - for (int j = 0; j < strideRemainder; j++) { - storage_buffers.out_data->data[dstOffset++] = uint8_t(0); - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/Shaders/ColorClear.metal b/src/Ryujinx.Graphics.Metal/Shaders/ColorClear.metal deleted file mode 100644 index 46a57e035..000000000 --- a/src/Ryujinx.Graphics.Metal/Shaders/ColorClear.metal +++ /dev/null @@ -1,38 +0,0 @@ -#include - -using namespace metal; - -struct VertexOut { - float4 position [[position]]; -}; - -struct ClearColor { - FORMAT4 data; -}; - -struct ConstantBuffers { - constant ClearColor* clear_color; -}; - -vertex VertexOut vertexMain(ushort vid [[vertex_id]]) { - int low = vid & 1; - int high = vid >> 1; - - VertexOut out; - - out.position.x = (float(low) - 0.5f) * 2.0f; - out.position.y = (float(high) - 0.5f) * 2.0f; - out.position.z = 0.0f; - out.position.w = 1.0f; - - return out; -} - -struct FragmentOut { - FORMAT4 color [[color(COLOR_ATTACHMENT_INDEX)]]; -}; - -fragment FragmentOut fragmentMain(VertexOut in [[stage_in]], - constant ConstantBuffers &constant_buffers [[buffer(CONSTANT_BUFFERS_INDEX)]]) { - return {constant_buffers.clear_color->data}; -} diff --git a/src/Ryujinx.Graphics.Metal/Shaders/ConvertD32S8ToD24S8.metal b/src/Ryujinx.Graphics.Metal/Shaders/ConvertD32S8ToD24S8.metal deleted file mode 100644 index 870ac3d78..000000000 --- a/src/Ryujinx.Graphics.Metal/Shaders/ConvertD32S8ToD24S8.metal +++ /dev/null @@ -1,66 +0,0 @@ -#include - -using namespace metal; - -struct StrideArguments { - int pixelCount; - int dstStartOffset; -}; - -struct InData { - uint data[1]; -}; - -struct OutData { - uint data[1]; -}; - -struct ConstantBuffers { - constant StrideArguments* stride_arguments; -}; - -struct StorageBuffers { - device InData* in_data; - device OutData* out_data; -}; - -kernel void kernelMain(constant ConstantBuffers &constant_buffers [[buffer(CONSTANT_BUFFERS_INDEX)]], - device StorageBuffers &storage_buffers [[buffer(STORAGE_BUFFERS_INDEX)]], - uint3 thread_position_in_grid [[thread_position_in_grid]], - uint3 threads_per_threadgroup [[threads_per_threadgroup]], - uint3 threadgroups_per_grid [[threadgroups_per_grid]]) -{ - // Determine what slice of the stride copies this invocation will perform. - int invocations = int(threads_per_threadgroup.x * threadgroups_per_grid.x); - - int copiesRequired = constant_buffers.stride_arguments->pixelCount; - - // Find the copies that this invocation should perform. - - // - Copies that all invocations perform. - int allInvocationCopies = copiesRequired / invocations; - - // - Extra remainder copy that this invocation performs. - int index = int(thread_position_in_grid.x); - int extra = (index < (copiesRequired % invocations)) ? 1 : 0; - - int copyCount = allInvocationCopies + extra; - - // Finally, get the starting offset. Make sure to count extra copies. - - int startCopy = allInvocationCopies * index + min(copiesRequired % invocations, index); - - int srcOffset = startCopy * 2; - int dstOffset = constant_buffers.stride_arguments->dstStartOffset + startCopy; - - // Perform the conversion for this region. - for (int i = 0; i < copyCount; i++) - { - float depth = as_type(storage_buffers.in_data->data[srcOffset++]); - uint stencil = storage_buffers.in_data->data[srcOffset++]; - - uint rescaledDepth = uint(clamp(depth, 0.0, 1.0) * 16777215.0); - - storage_buffers.out_data->data[dstOffset++] = (rescaledDepth << 8) | (stencil & 0xff); - } -} diff --git a/src/Ryujinx.Graphics.Metal/Shaders/ConvertIndexBuffer.metal b/src/Ryujinx.Graphics.Metal/Shaders/ConvertIndexBuffer.metal deleted file mode 100644 index c8fee5818..000000000 --- a/src/Ryujinx.Graphics.Metal/Shaders/ConvertIndexBuffer.metal +++ /dev/null @@ -1,59 +0,0 @@ -#include - -using namespace metal; - -struct IndexBufferPattern { - int pattern[8]; - int primitiveVertices; - int primitiveVerticesOut; - int indexSize; - int indexSizeOut; - int baseIndex; - int indexStride; - int srcOffset; - int totalPrimitives; -}; - -struct InData { - uint8_t data[1]; -}; - -struct OutData { - uint8_t data[1]; -}; - -struct StorageBuffers { - device InData* in_data; - device OutData* out_data; - constant IndexBufferPattern* index_buffer_pattern; -}; - -kernel void kernelMain(device StorageBuffers &storage_buffers [[buffer(STORAGE_BUFFERS_INDEX)]], - uint3 thread_position_in_grid [[thread_position_in_grid]]) -{ - int primitiveIndex = int(thread_position_in_grid.x); - if (primitiveIndex >= storage_buffers.index_buffer_pattern->totalPrimitives) - { - return; - } - - int inOffset = primitiveIndex * storage_buffers.index_buffer_pattern->indexStride; - int outOffset = primitiveIndex * storage_buffers.index_buffer_pattern->primitiveVerticesOut; - - for (int i = 0; i < storage_buffers.index_buffer_pattern->primitiveVerticesOut; i++) - { - int j; - int io = max(0, inOffset + storage_buffers.index_buffer_pattern->baseIndex + storage_buffers.index_buffer_pattern->pattern[i]) * storage_buffers.index_buffer_pattern->indexSize; - int oo = (outOffset + i) * storage_buffers.index_buffer_pattern->indexSizeOut; - - for (j = 0; j < storage_buffers.index_buffer_pattern->indexSize; j++) - { - storage_buffers.out_data->data[oo + j] = storage_buffers.in_data->data[storage_buffers.index_buffer_pattern->srcOffset + io + j]; - } - - for(; j < storage_buffers.index_buffer_pattern->indexSizeOut; j++) - { - storage_buffers.out_data->data[oo + j] = uint8_t(0); - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/Shaders/DepthBlit.metal b/src/Ryujinx.Graphics.Metal/Shaders/DepthBlit.metal deleted file mode 100644 index 8b8467c2f..000000000 --- a/src/Ryujinx.Graphics.Metal/Shaders/DepthBlit.metal +++ /dev/null @@ -1,27 +0,0 @@ -#include - -using namespace metal; - -struct CopyVertexOut { - float4 position [[position]]; - float2 uv; -}; - -struct Textures -{ - texture2d texture; - sampler sampler; -}; - -struct FragmentOut { - float depth [[depth(any)]]; -}; - -fragment FragmentOut fragmentMain(CopyVertexOut in [[stage_in]], - constant Textures &textures [[buffer(TEXTURES_INDEX)]]) { - FragmentOut out; - - out.depth = textures.texture.sample(textures.sampler, in.uv).r; - - return out; -} diff --git a/src/Ryujinx.Graphics.Metal/Shaders/DepthBlitMs.metal b/src/Ryujinx.Graphics.Metal/Shaders/DepthBlitMs.metal deleted file mode 100644 index 10791f636..000000000 --- a/src/Ryujinx.Graphics.Metal/Shaders/DepthBlitMs.metal +++ /dev/null @@ -1,29 +0,0 @@ -#include - -using namespace metal; - -struct CopyVertexOut { - float4 position [[position]]; - float2 uv; -}; - -struct Textures -{ - texture2d_ms texture; -}; - -struct FragmentOut { - float depth [[depth(any)]]; -}; - -fragment FragmentOut fragmentMain(CopyVertexOut in [[stage_in]], - constant Textures &textures [[buffer(TEXTURES_INDEX)]], - uint sample_id [[sample_id]]) { - FragmentOut out; - - uint2 tex_size = uint2(textures.texture.get_width(), textures.texture.get_height()); - uint2 tex_coord = uint2(in.uv * float2(tex_size)); - out.depth = textures.texture.read(tex_coord, sample_id).r; - - return out; -} diff --git a/src/Ryujinx.Graphics.Metal/Shaders/DepthStencilClear.metal b/src/Ryujinx.Graphics.Metal/Shaders/DepthStencilClear.metal deleted file mode 100644 index 7e50f2ce7..000000000 --- a/src/Ryujinx.Graphics.Metal/Shaders/DepthStencilClear.metal +++ /dev/null @@ -1,42 +0,0 @@ -#include - -using namespace metal; - -struct VertexOut { - float4 position [[position]]; -}; - -struct FragmentOut { - float depth [[depth(any)]]; -}; - -struct ClearDepth { - float data; -}; - -struct ConstantBuffers { - constant ClearDepth* clear_depth; -}; - -vertex VertexOut vertexMain(ushort vid [[vertex_id]]) { - int low = vid & 1; - int high = vid >> 1; - - VertexOut out; - - out.position.x = (float(low) - 0.5f) * 2.0f; - out.position.y = (float(high) - 0.5f) * 2.0f; - out.position.z = 0.0f; - out.position.w = 1.0f; - - return out; -} - -fragment FragmentOut fragmentMain(VertexOut in [[stage_in]], - constant ConstantBuffers &constant_buffers [[buffer(CONSTANT_BUFFERS_INDEX)]]) { - FragmentOut out; - - out.depth = constant_buffers.clear_depth->data; - - return out; -} diff --git a/src/Ryujinx.Graphics.Metal/Shaders/StencilBlit.metal b/src/Ryujinx.Graphics.Metal/Shaders/StencilBlit.metal deleted file mode 100644 index 0b25f322d..000000000 --- a/src/Ryujinx.Graphics.Metal/Shaders/StencilBlit.metal +++ /dev/null @@ -1,27 +0,0 @@ -#include - -using namespace metal; - -struct CopyVertexOut { - float4 position [[position]]; - float2 uv; -}; - -struct Textures -{ - texture2d texture; - sampler sampler; -}; - -struct FragmentOut { - uint stencil [[stencil]]; -}; - -fragment FragmentOut fragmentMain(CopyVertexOut in [[stage_in]], - constant Textures &textures [[buffer(TEXTURES_INDEX)]]) { - FragmentOut out; - - out.stencil = textures.texture.sample(textures.sampler, in.uv).r; - - return out; -} diff --git a/src/Ryujinx.Graphics.Metal/Shaders/StencilBlitMs.metal b/src/Ryujinx.Graphics.Metal/Shaders/StencilBlitMs.metal deleted file mode 100644 index e7f2d20b7..000000000 --- a/src/Ryujinx.Graphics.Metal/Shaders/StencilBlitMs.metal +++ /dev/null @@ -1,29 +0,0 @@ -#include - -using namespace metal; - -struct CopyVertexOut { - float4 position [[position]]; - float2 uv; -}; - -struct Textures -{ - texture2d_ms texture; -}; - -struct FragmentOut { - uint stencil [[stencil]]; -}; - -fragment FragmentOut fragmentMain(CopyVertexOut in [[stage_in]], - constant Textures &textures [[buffer(TEXTURES_INDEX)]], - uint sample_id [[sample_id]]) { - FragmentOut out; - - uint2 tex_size = uint2(textures.texture.get_width(), textures.texture.get_height()); - uint2 tex_coord = uint2(in.uv * float2(tex_size)); - out.stencil = textures.texture.read(tex_coord, sample_id).r; - - return out; -} diff --git a/src/Ryujinx.Graphics.Metal/StagingBuffer.cs b/src/Ryujinx.Graphics.Metal/StagingBuffer.cs deleted file mode 100644 index b4838ee33..000000000 --- a/src/Ryujinx.Graphics.Metal/StagingBuffer.cs +++ /dev/null @@ -1,288 +0,0 @@ -using Ryujinx.Common; -using Ryujinx.Common.Logging; -using Ryujinx.Graphics.GAL; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - readonly struct StagingBufferReserved - { - public readonly BufferHolder Buffer; - public readonly int Offset; - public readonly int Size; - - public StagingBufferReserved(BufferHolder buffer, int offset, int size) - { - Buffer = buffer; - Offset = offset; - Size = size; - } - } - - [SupportedOSPlatform("macos")] - class StagingBuffer : IDisposable - { - private const int BufferSize = 32 * 1024 * 1024; - - private int _freeOffset; - private int _freeSize; - - private readonly MetalRenderer _renderer; - private readonly BufferHolder _buffer; - private readonly int _resourceAlignment; - - public readonly BufferHandle Handle; - - private readonly struct PendingCopy - { - public FenceHolder Fence { get; } - public int Size { get; } - - public PendingCopy(FenceHolder fence, int size) - { - Fence = fence; - Size = size; - fence.Get(); - } - } - - private readonly Queue _pendingCopies; - - public StagingBuffer(MetalRenderer renderer, BufferManager bufferManager) - { - _renderer = renderer; - - Handle = bufferManager.CreateWithHandle(BufferSize, out _buffer); - _pendingCopies = new Queue(); - _freeSize = BufferSize; - _resourceAlignment = Constants.MinResourceAlignment; - } - - public void PushData(CommandBufferPool cbp, CommandBufferScoped? cbs, BufferHolder dst, int dstOffset, ReadOnlySpan data) - { - bool isRender = cbs != null; - CommandBufferScoped scoped = cbs ?? cbp.Rent(); - - // Must push all data to the buffer. If it can't fit, split it up. - - while (data.Length > 0) - { - if (_freeSize < data.Length) - { - FreeCompleted(); - } - - while (_freeSize == 0) - { - if (!WaitFreeCompleted(cbp)) - { - if (isRender) - { - _renderer.FlushAllCommands(); - scoped = cbp.Rent(); - isRender = false; - } - else - { - scoped = cbp.ReturnAndRent(scoped); - } - } - } - - int chunkSize = Math.Min(_freeSize, data.Length); - - PushDataImpl(scoped, dst, dstOffset, data[..chunkSize]); - - dstOffset += chunkSize; - data = data[chunkSize..]; - } - - if (!isRender) - { - scoped.Dispose(); - } - } - - private void PushDataImpl(CommandBufferScoped cbs, BufferHolder dst, int dstOffset, ReadOnlySpan data) - { - Auto srcBuffer = _buffer.GetBuffer(); - Auto dstBuffer = dst.GetBuffer(dstOffset, data.Length, true); - - int offset = _freeOffset; - int capacity = BufferSize - offset; - if (capacity < data.Length) - { - _buffer.SetDataUnchecked(offset, data[..capacity]); - _buffer.SetDataUnchecked(0, data[capacity..]); - - BufferHolder.Copy(cbs, srcBuffer, dstBuffer, offset, dstOffset, capacity); - BufferHolder.Copy(cbs, srcBuffer, dstBuffer, 0, dstOffset + capacity, data.Length - capacity); - } - else - { - _buffer.SetDataUnchecked(offset, data); - - BufferHolder.Copy(cbs, srcBuffer, dstBuffer, offset, dstOffset, data.Length); - } - - _freeOffset = (offset + data.Length) & (BufferSize - 1); - _freeSize -= data.Length; - Debug.Assert(_freeSize >= 0); - - _pendingCopies.Enqueue(new PendingCopy(cbs.GetFence(), data.Length)); - } - - public bool TryPushData(CommandBufferScoped cbs, BufferHolder dst, int dstOffset, ReadOnlySpan data) - { - if (data.Length > BufferSize) - { - return false; - } - - if (_freeSize < data.Length) - { - FreeCompleted(); - - if (_freeSize < data.Length) - { - return false; - } - } - - PushDataImpl(cbs, dst, dstOffset, data); - - return true; - } - - private StagingBufferReserved ReserveDataImpl(CommandBufferScoped cbs, int size, int alignment) - { - // Assumes the caller has already determined that there is enough space. - int offset = BitUtils.AlignUp(_freeOffset, alignment); - int padding = offset - _freeOffset; - - int capacity = Math.Min(_freeSize, BufferSize - offset); - int reservedLength = size + padding; - if (capacity < size) - { - offset = 0; // Place at start. - reservedLength += capacity; - } - - _freeOffset = (_freeOffset + reservedLength) & (BufferSize - 1); - _freeSize -= reservedLength; - Debug.Assert(_freeSize >= 0); - - _pendingCopies.Enqueue(new PendingCopy(cbs.GetFence(), reservedLength)); - - return new StagingBufferReserved(_buffer, offset, size); - } - - private int GetContiguousFreeSize(int alignment) - { - int alignedFreeOffset = BitUtils.AlignUp(_freeOffset, alignment); - int padding = alignedFreeOffset - _freeOffset; - - // Free regions: - // - Aligned free offset to end (minimum free size - padding) - // - 0 to _freeOffset + freeSize wrapped (only if free area contains 0) - - int endOffset = (_freeOffset + _freeSize) & (BufferSize - 1); - - return Math.Max( - Math.Min(_freeSize - padding, BufferSize - alignedFreeOffset), - endOffset <= _freeOffset ? Math.Min(_freeSize, endOffset) : 0 - ); - } - - /// - /// Reserve a range on the staging buffer for the current command buffer and upload data to it. - /// - /// Command buffer to reserve the data on - /// The minimum size the reserved data requires - /// The required alignment for the buffer offset - /// The reserved range of the staging buffer - public StagingBufferReserved? TryReserveData(CommandBufferScoped cbs, int size, int alignment) - { - if (size > BufferSize) - { - return null; - } - - // Temporary reserved data cannot be fragmented. - - if (GetContiguousFreeSize(alignment) < size) - { - FreeCompleted(); - - if (GetContiguousFreeSize(alignment) < size) - { - Logger.Debug?.PrintMsg(LogClass.Gpu, $"Staging buffer out of space to reserve data of size {size}."); - return null; - } - } - - return ReserveDataImpl(cbs, size, alignment); - } - - /// - /// Reserve a range on the staging buffer for the current command buffer and upload data to it. - /// Uses the most permissive byte alignment. - /// - /// Command buffer to reserve the data on - /// The minimum size the reserved data requires - /// The reserved range of the staging buffer - public StagingBufferReserved? TryReserveData(CommandBufferScoped cbs, int size) - { - return TryReserveData(cbs, size, _resourceAlignment); - } - - private bool WaitFreeCompleted(CommandBufferPool cbp) - { - if (_pendingCopies.TryPeek(out PendingCopy pc)) - { - if (!pc.Fence.IsSignaled()) - { - if (cbp.IsFenceOnRentedCommandBuffer(pc.Fence)) - { - return false; - } - - pc.Fence.Wait(); - } - - PendingCopy dequeued = _pendingCopies.Dequeue(); - Debug.Assert(dequeued.Fence == pc.Fence); - _freeSize += pc.Size; - pc.Fence.Put(); - } - - return true; - } - - public void FreeCompleted() - { - FenceHolder signalledFence = null; - while (_pendingCopies.TryPeek(out PendingCopy pc) && (pc.Fence == signalledFence || pc.Fence.IsSignaled())) - { - signalledFence = pc.Fence; // Already checked - don't need to do it again. - PendingCopy dequeued = _pendingCopies.Dequeue(); - Debug.Assert(dequeued.Fence == pc.Fence); - _freeSize += pc.Size; - pc.Fence.Put(); - } - } - - public void Dispose() - { - _renderer.BufferManager.Delete(Handle); - - while (_pendingCopies.TryDequeue(out PendingCopy pc)) - { - pc.Fence.Put(); - } - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/State/DepthStencilUid.cs b/src/Ryujinx.Graphics.Metal/State/DepthStencilUid.cs deleted file mode 100644 index 63b1d8ef4..000000000 --- a/src/Ryujinx.Graphics.Metal/State/DepthStencilUid.cs +++ /dev/null @@ -1,110 +0,0 @@ -using SharpMetal.Metal; -using System; -using System.Runtime.InteropServices; -using System.Runtime.Intrinsics; - -namespace Ryujinx.Graphics.Metal.State -{ - [StructLayout(LayoutKind.Sequential, Pack = 1)] - public struct StencilUid - { - public uint ReadMask; - public uint WriteMask; - public ushort Operations; - - public MTLStencilOperation StencilFailureOperation - { - readonly get => (MTLStencilOperation)((Operations >> 0) & 0xF); - set => Operations = (ushort)((Operations & 0xFFF0) | ((int)value << 0)); - } - - public MTLStencilOperation DepthFailureOperation - { - readonly get => (MTLStencilOperation)((Operations >> 4) & 0xF); - set => Operations = (ushort)((Operations & 0xFF0F) | ((int)value << 4)); - } - - public MTLStencilOperation DepthStencilPassOperation - { - readonly get => (MTLStencilOperation)((Operations >> 8) & 0xF); - set => Operations = (ushort)((Operations & 0xF0FF) | ((int)value << 8)); - } - - public MTLCompareFunction StencilCompareFunction - { - readonly get => (MTLCompareFunction)((Operations >> 12) & 0xF); - set => Operations = (ushort)((Operations & 0x0FFF) | ((int)value << 12)); - } - } - - - [StructLayout(LayoutKind.Explicit, Size = 24)] - internal struct DepthStencilUid : IEquatable - { - [FieldOffset(0)] - public StencilUid FrontFace; - - [FieldOffset(10)] - public ushort DepthState; - - [FieldOffset(12)] - public StencilUid BackFace; - - [FieldOffset(22)] - private readonly ushort _padding; - - // Quick access aliases -#pragma warning disable IDE0044 // Add readonly modifier - [FieldOffset(0)] - private ulong _id0; - [FieldOffset(8)] - private ulong _id1; - [FieldOffset(0)] - private Vector128 _id01; - [FieldOffset(16)] - private ulong _id2; -#pragma warning restore IDE0044 // Add readonly modifier - - public MTLCompareFunction DepthCompareFunction - { - readonly get => (MTLCompareFunction)((DepthState >> 0) & 0xF); - set => DepthState = (ushort)((DepthState & 0xFFF0) | ((int)value << 0)); - } - - public bool StencilTestEnabled - { - readonly get => ((DepthState >> 4) & 0x1) != 0; - set => DepthState = (ushort)((DepthState & 0xFFEF) | ((value ? 1 : 0) << 4)); - } - - public bool DepthWriteEnabled - { - readonly get => ((DepthState >> 15) & 0x1) != 0; - set => DepthState = (ushort)((DepthState & 0x7FFF) | ((value ? 1 : 0) << 15)); - } - - public readonly override bool Equals(object obj) - { - return obj is DepthStencilUid other && EqualsRef(ref other); - } - - public readonly bool EqualsRef(ref DepthStencilUid other) - { - return _id01.Equals(other._id01) && _id2 == other._id2; - } - - public readonly bool Equals(DepthStencilUid other) - { - return EqualsRef(ref other); - } - - public readonly override int GetHashCode() - { - ulong hash64 = _id0 * 23 ^ - _id1 * 23 ^ - _id2 * 23; - - return (int)hash64 ^ ((int)(hash64 >> 32) * 17); - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/State/PipelineState.cs b/src/Ryujinx.Graphics.Metal/State/PipelineState.cs deleted file mode 100644 index 14073dbe1..000000000 --- a/src/Ryujinx.Graphics.Metal/State/PipelineState.cs +++ /dev/null @@ -1,341 +0,0 @@ -using Ryujinx.Common.Logging; -using Ryujinx.Graphics.GAL; -using SharpMetal.Foundation; -using SharpMetal.Metal; -using System; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - struct PipelineState - { - public PipelineUid Internal; - - public uint StagesCount - { - readonly get => (byte)((Internal.Id0 >> 0) & 0xFF); - set => Internal.Id0 = (Internal.Id0 & 0xFFFFFFFFFFFFFF00) | ((ulong)value << 0); - } - - public uint VertexAttributeDescriptionsCount - { - readonly get => (byte)((Internal.Id0 >> 8) & 0xFF); - set => Internal.Id0 = (Internal.Id0 & 0xFFFFFFFFFFFF00FF) | ((ulong)value << 8); - } - - public uint VertexBindingDescriptionsCount - { - readonly get => (byte)((Internal.Id0 >> 16) & 0xFF); - set => Internal.Id0 = (Internal.Id0 & 0xFFFFFFFFFF00FFFF) | ((ulong)value << 16); - } - - public uint ColorBlendAttachmentStateCount - { - readonly get => (byte)((Internal.Id0 >> 24) & 0xFF); - set => Internal.Id0 = (Internal.Id0 & 0xFFFFFFFF00FFFFFF) | ((ulong)value << 24); - } - - /* - * Can be an input to a pipeline, but not sure what the situation for that is. - public PrimitiveTopology Topology - { - readonly get => (PrimitiveTopology)((Internal.Id6 >> 16) & 0xF); - set => Internal.Id6 = (Internal.Id6 & 0xFFFFFFFFFFF0FFFF) | ((ulong)value << 16); - } - */ - - public MTLLogicOperation LogicOp - { - readonly get => (MTLLogicOperation)((Internal.Id0 >> 32) & 0xF); - set => Internal.Id0 = (Internal.Id0 & 0xFFFFFFF0FFFFFFFF) | ((ulong)value << 32); - } - - //? - public bool PrimitiveRestartEnable - { - readonly get => ((Internal.Id0 >> 36) & 0x1) != 0UL; - set => Internal.Id0 = (Internal.Id0 & 0xFFFFFFEFFFFFFFFF) | ((value ? 1UL : 0UL) << 36); - } - - public bool RasterizerDiscardEnable - { - readonly get => ((Internal.Id0 >> 37) & 0x1) != 0UL; - set => Internal.Id0 = (Internal.Id0 & 0xFFFFFFDFFFFFFFFF) | ((value ? 1UL : 0UL) << 37); - } - - public bool LogicOpEnable - { - readonly get => ((Internal.Id0 >> 38) & 0x1) != 0UL; - set => Internal.Id0 = (Internal.Id0 & 0xFFFFFFBFFFFFFFFF) | ((value ? 1UL : 0UL) << 38); - } - - public bool AlphaToCoverageEnable - { - readonly get => ((Internal.Id0 >> 40) & 0x1) != 0UL; - set => Internal.Id0 = (Internal.Id0 & 0xFFFFFEFFFFFFFFFF) | ((value ? 1UL : 0UL) << 40); - } - - public bool AlphaToOneEnable - { - readonly get => ((Internal.Id0 >> 41) & 0x1) != 0UL; - set => Internal.Id0 = (Internal.Id0 & 0xFFFFFDFFFFFFFFFF) | ((value ? 1UL : 0UL) << 41); - } - - public MTLPixelFormat DepthStencilFormat - { - readonly get => (MTLPixelFormat)(Internal.Id0 >> 48); - set => Internal.Id0 = (Internal.Id0 & 0x0000FFFFFFFFFFFF) | ((ulong)value << 48); - } - - // Not sure how to appropriately use this, but it does need to be passed for tess. - public uint PatchControlPoints - { - readonly get => (uint)((Internal.Id1 >> 0) & 0xFFFFFFFF); - set => Internal.Id1 = (Internal.Id1 & 0xFFFFFFFF00000000) | ((ulong)value << 0); - } - - public uint SamplesCount - { - readonly get => (uint)((Internal.Id1 >> 32) & 0xFFFFFFFF); - set => Internal.Id1 = (Internal.Id1 & 0xFFFFFFFF) | ((ulong)value << 32); - } - - // Advanced blend not supported - - private readonly void BuildColorAttachment(MTLRenderPipelineColorAttachmentDescriptor descriptor, ColorBlendStateUid blendState) - { - descriptor.PixelFormat = blendState.PixelFormat; - descriptor.SetBlendingEnabled(blendState.Enable); - descriptor.AlphaBlendOperation = blendState.AlphaBlendOperation; - descriptor.RgbBlendOperation = blendState.RgbBlendOperation; - descriptor.SourceAlphaBlendFactor = blendState.SourceAlphaBlendFactor; - descriptor.DestinationAlphaBlendFactor = blendState.DestinationAlphaBlendFactor; - descriptor.SourceRGBBlendFactor = blendState.SourceRGBBlendFactor; - descriptor.DestinationRGBBlendFactor = blendState.DestinationRGBBlendFactor; - descriptor.WriteMask = blendState.WriteMask; - } - - private readonly MTLVertexDescriptor BuildVertexDescriptor() - { - MTLVertexDescriptor vertexDescriptor = new(); - - for (int i = 0; i < VertexAttributeDescriptionsCount; i++) - { - VertexInputAttributeUid uid = Internal.VertexAttributes[i]; - - MTLVertexAttributeDescriptor attrib = vertexDescriptor.Attributes.Object((ulong)i); - attrib.Format = uid.Format; - attrib.Offset = uid.Offset; - attrib.BufferIndex = uid.BufferIndex; - } - - for (int i = 0; i < VertexBindingDescriptionsCount; i++) - { - VertexInputLayoutUid uid = Internal.VertexBindings[i]; - - MTLVertexBufferLayoutDescriptor layout = vertexDescriptor.Layouts.Object((ulong)i); - - layout.StepFunction = uid.StepFunction; - layout.StepRate = uid.StepRate; - layout.Stride = uid.Stride; - } - - return vertexDescriptor; - } - - private MTLRenderPipelineDescriptor CreateRenderDescriptor(Program program) - { - MTLRenderPipelineDescriptor renderPipelineDescriptor = new(); - - for (int i = 0; i < Constants.MaxColorAttachments; i++) - { - ColorBlendStateUid blendState = Internal.ColorBlendState[i]; - - if (blendState.PixelFormat != MTLPixelFormat.Invalid) - { - MTLRenderPipelineColorAttachmentDescriptor pipelineAttachment = renderPipelineDescriptor.ColorAttachments.Object((ulong)i); - - BuildColorAttachment(pipelineAttachment, blendState); - } - } - - MTLPixelFormat dsFormat = DepthStencilFormat; - if (dsFormat != MTLPixelFormat.Invalid) - { - switch (dsFormat) - { - // Depth Only Attachment - case MTLPixelFormat.Depth16Unorm: - case MTLPixelFormat.Depth32Float: - renderPipelineDescriptor.DepthAttachmentPixelFormat = dsFormat; - break; - - // Stencil Only Attachment - case MTLPixelFormat.Stencil8: - renderPipelineDescriptor.StencilAttachmentPixelFormat = dsFormat; - break; - - // Combined Attachment - case MTLPixelFormat.Depth24UnormStencil8: - case MTLPixelFormat.Depth32FloatStencil8: - renderPipelineDescriptor.DepthAttachmentPixelFormat = dsFormat; - renderPipelineDescriptor.StencilAttachmentPixelFormat = dsFormat; - break; - default: - Logger.Error?.PrintMsg(LogClass.Gpu, $"Unsupported Depth/Stencil Format: {dsFormat}!"); - break; - } - } - - renderPipelineDescriptor.LogicOperationEnabled = LogicOpEnable; - renderPipelineDescriptor.LogicOperation = LogicOp; - renderPipelineDescriptor.AlphaToCoverageEnabled = AlphaToCoverageEnable; - renderPipelineDescriptor.AlphaToOneEnabled = AlphaToOneEnable; - renderPipelineDescriptor.RasterizationEnabled = !RasterizerDiscardEnable; - renderPipelineDescriptor.SampleCount = Math.Max(1, SamplesCount); - - MTLVertexDescriptor vertexDescriptor = BuildVertexDescriptor(); - renderPipelineDescriptor.VertexDescriptor = vertexDescriptor; - - renderPipelineDescriptor.VertexFunction = program.VertexFunction; - - if (program.FragmentFunction.NativePtr != 0) - { - renderPipelineDescriptor.FragmentFunction = program.FragmentFunction; - } - - return renderPipelineDescriptor; - } - - public MTLRenderPipelineState CreateRenderPipeline(MTLDevice device, Program program) - { - if (program.TryGetGraphicsPipeline(ref Internal, out MTLRenderPipelineState pipelineState)) - { - return pipelineState; - } - - using MTLRenderPipelineDescriptor descriptor = CreateRenderDescriptor(program); - - NSError error = new(IntPtr.Zero); - pipelineState = device.NewRenderPipelineState(descriptor, ref error); - if (error != IntPtr.Zero) - { - Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create Render Pipeline State: {StringHelper.String(error.LocalizedDescription)}"); - } - - program.AddGraphicsPipeline(ref Internal, pipelineState); - - return pipelineState; - } - - public static MTLComputePipelineDescriptor CreateComputeDescriptor(Program program) - { - ComputeSize localSize = program.ComputeLocalSize; - - uint maxThreads = (uint)(localSize.X * localSize.Y * localSize.Z); - - if (maxThreads == 0) - { - throw new InvalidOperationException($"Local thread size for compute cannot be 0 in any dimension."); - } - - MTLComputePipelineDescriptor descriptor = new() - { - ComputeFunction = program.ComputeFunction, - MaxTotalThreadsPerThreadgroup = maxThreads, - ThreadGroupSizeIsMultipleOfThreadExecutionWidth = true, - }; - - return descriptor; - } - - public static MTLComputePipelineState CreateComputePipeline(MTLDevice device, Program program) - { - if (program.TryGetComputePipeline(out MTLComputePipelineState pipelineState)) - { - return pipelineState; - } - - using MTLComputePipelineDescriptor descriptor = CreateComputeDescriptor(program); - - NSError error = new(IntPtr.Zero); - pipelineState = device.NewComputePipelineState(descriptor, MTLPipelineOption.None, 0, ref error); - if (error != IntPtr.Zero) - { - Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create Compute Pipeline State: {StringHelper.String(error.LocalizedDescription)}"); - } - - program.AddComputePipeline(pipelineState); - - return pipelineState; - } - - public void Initialize() - { - SamplesCount = 1; - - Internal.ResetColorState(); - } - - /* - * TODO, this is from vulkan. - - private void UpdateVertexAttributeDescriptions(VulkanRenderer gd) - { - // Vertex attributes exceeding the stride are invalid. - // In metal, they cause glitches with the vertex shader fetching incorrect values. - // To work around this, we reduce the format to something that doesn't exceed the stride if possible. - // The assumption is that the exceeding components are not actually accessed on the shader. - - for (int index = 0; index < VertexAttributeDescriptionsCount; index++) - { - var attribute = Internal.VertexAttributeDescriptions[index]; - int vbIndex = GetVertexBufferIndex(attribute.Binding); - - if (vbIndex >= 0) - { - ref var vb = ref Internal.VertexBindingDescriptions[vbIndex]; - - Format format = attribute.Format; - - while (vb.Stride != 0 && attribute.Offset + FormatTable.GetAttributeFormatSize(format) > vb.Stride) - { - Format newFormat = FormatTable.DropLastComponent(format); - - if (newFormat == format) - { - // That case means we failed to find a format that fits within the stride, - // so just restore the original format and give up. - format = attribute.Format; - break; - } - - format = newFormat; - } - - if (attribute.Format != format && gd.FormatCapabilities.BufferFormatSupports(FormatFeatureFlags.VertexBufferBit, format)) - { - attribute.Format = format; - } - } - - _vertexAttributeDescriptions2[index] = attribute; - } - } - - private int GetVertexBufferIndex(uint binding) - { - for (int index = 0; index < VertexBindingDescriptionsCount; index++) - { - if (Internal.VertexBindingDescriptions[index].Binding == binding) - { - return index; - } - } - - return -1; - } - */ - } -} diff --git a/src/Ryujinx.Graphics.Metal/State/PipelineUid.cs b/src/Ryujinx.Graphics.Metal/State/PipelineUid.cs deleted file mode 100644 index 5b514c2c9..000000000 --- a/src/Ryujinx.Graphics.Metal/State/PipelineUid.cs +++ /dev/null @@ -1,208 +0,0 @@ -using Ryujinx.Common.Memory; -using SharpMetal.Metal; -using System; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Runtime.Intrinsics; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - struct VertexInputAttributeUid - { - public ulong Id0; - - public ulong Offset - { - readonly get => (uint)((Id0 >> 0) & 0xFFFFFFFF); - set => Id0 = (Id0 & 0xFFFFFFFF00000000) | ((ulong)value << 0); - } - - public MTLVertexFormat Format - { - readonly get => (MTLVertexFormat)((Id0 >> 32) & 0xFFFF); - set => Id0 = (Id0 & 0xFFFF0000FFFFFFFF) | ((ulong)value << 32); - } - - public ulong BufferIndex - { - readonly get => ((Id0 >> 48) & 0xFFFF); - set => Id0 = (Id0 & 0x0000FFFFFFFFFFFF) | ((ulong)value << 48); - } - } - - struct VertexInputLayoutUid - { - public ulong Id0; - - public uint Stride - { - readonly get => (uint)((Id0 >> 0) & 0xFFFFFFFF); - set => Id0 = (Id0 & 0xFFFFFFFF00000000) | ((ulong)value << 0); - } - - public uint StepRate - { - readonly get => (uint)((Id0 >> 32) & 0x1FFFFFFF); - set => Id0 = (Id0 & 0xE0000000FFFFFFFF) | ((ulong)value << 32); - } - - public MTLVertexStepFunction StepFunction - { - readonly get => (MTLVertexStepFunction)((Id0 >> 61) & 0x7); - set => Id0 = (Id0 & 0x1FFFFFFFFFFFFFFF) | ((ulong)value << 61); - } - } - - struct ColorBlendStateUid - { - public ulong Id0; - - public MTLPixelFormat PixelFormat - { - readonly get => (MTLPixelFormat)((Id0 >> 0) & 0xFFFF); - set => Id0 = (Id0 & 0xFFFFFFFFFFFF0000) | ((ulong)value << 0); - } - - public MTLBlendFactor SourceRGBBlendFactor - { - readonly get => (MTLBlendFactor)((Id0 >> 16) & 0xFF); - set => Id0 = (Id0 & 0xFFFFFFFFFF00FFFF) | ((ulong)value << 16); - } - - public MTLBlendFactor DestinationRGBBlendFactor - { - readonly get => (MTLBlendFactor)((Id0 >> 24) & 0xFF); - set => Id0 = (Id0 & 0xFFFFFFFF00FFFFFF) | ((ulong)value << 24); - } - - public MTLBlendOperation RgbBlendOperation - { - readonly get => (MTLBlendOperation)((Id0 >> 32) & 0xF); - set => Id0 = (Id0 & 0xFFFFFFF0FFFFFFFF) | ((ulong)value << 32); - } - - public MTLBlendOperation AlphaBlendOperation - { - readonly get => (MTLBlendOperation)((Id0 >> 36) & 0xF); - set => Id0 = (Id0 & 0xFFFFFF0FFFFFFFFF) | ((ulong)value << 36); - } - - public MTLBlendFactor SourceAlphaBlendFactor - { - readonly get => (MTLBlendFactor)((Id0 >> 40) & 0xFF); - set => Id0 = (Id0 & 0xFFFF00FFFFFFFFFF) | ((ulong)value << 40); - } - - public MTLBlendFactor DestinationAlphaBlendFactor - { - readonly get => (MTLBlendFactor)((Id0 >> 48) & 0xFF); - set => Id0 = (Id0 & 0xFF00FFFFFFFFFFFF) | ((ulong)value << 48); - } - - public MTLColorWriteMask WriteMask - { - readonly get => (MTLColorWriteMask)((Id0 >> 56) & 0xF); - set => Id0 = (Id0 & 0xF0FFFFFFFFFFFFFF) | ((ulong)value << 56); - } - - public bool Enable - { - readonly get => ((Id0 >> 63) & 0x1) != 0UL; - set => Id0 = (Id0 & 0x7FFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 63); - } - - public void Swap(ColorBlendStateUid uid) - { - MTLPixelFormat format = PixelFormat; - - this = uid; - PixelFormat = format; - } - } - - [SupportedOSPlatform("macos")] - struct PipelineUid : IRefEquatable - { - public ulong Id0; - public ulong Id1; - - private readonly uint VertexAttributeDescriptionsCount => (byte)((Id0 >> 8) & 0xFF); - private readonly uint VertexBindingDescriptionsCount => (byte)((Id0 >> 16) & 0xFF); - private readonly uint ColorBlendAttachmentStateCount => (byte)((Id0 >> 24) & 0xFF); - - public Array32 VertexAttributes; - public Array33 VertexBindings; - public Array8 ColorBlendState; - public uint AttachmentIntegerFormatMask; - public bool LogicOpsAllowed; - - public void ResetColorState() - { - ColorBlendState = new(); - - for (int i = 0; i < ColorBlendState.Length; i++) - { - ColorBlendState[i].WriteMask = MTLColorWriteMask.All; - } - } - - public readonly override bool Equals(object obj) - { - return obj is PipelineUid other && Equals(other); - } - - public bool Equals(ref PipelineUid other) - { - if (!Unsafe.As>(ref Id0).Equals(Unsafe.As>(ref other.Id0))) - { - return false; - } - - if (!SequenceEqual(VertexAttributes.AsSpan(), other.VertexAttributes.AsSpan(), VertexAttributeDescriptionsCount)) - { - return false; - } - - if (!SequenceEqual(VertexBindings.AsSpan(), other.VertexBindings.AsSpan(), VertexBindingDescriptionsCount)) - { - return false; - } - - if (!SequenceEqual(ColorBlendState.AsSpan(), other.ColorBlendState.AsSpan(), ColorBlendAttachmentStateCount)) - { - return false; - } - - return true; - } - - private static bool SequenceEqual(ReadOnlySpan x, ReadOnlySpan y, uint count) where T : unmanaged - { - return MemoryMarshal.Cast(x[..(int)count]).SequenceEqual(MemoryMarshal.Cast(y[..(int)count])); - } - - public override int GetHashCode() - { - ulong hash64 = Id0 * 23 ^ - Id1 * 23; - - for (int i = 0; i < (int)VertexAttributeDescriptionsCount; i++) - { - hash64 ^= VertexAttributes[i].Id0 * 23; - } - - for (int i = 0; i < (int)VertexBindingDescriptionsCount; i++) - { - hash64 ^= VertexBindings[i].Id0 * 23; - } - - for (int i = 0; i < (int)ColorBlendAttachmentStateCount; i++) - { - hash64 ^= ColorBlendState[i].Id0 * 23; - } - - return (int)hash64 ^ ((int)(hash64 >> 32) * 17); - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/StateCache.cs b/src/Ryujinx.Graphics.Metal/StateCache.cs deleted file mode 100644 index 8a9d175f1..000000000 --- a/src/Ryujinx.Graphics.Metal/StateCache.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - abstract class StateCache : IDisposable where T : IDisposable - { - private readonly Dictionary _cache = new(); - - protected abstract THash GetHash(TDescriptor descriptor); - - protected abstract T CreateValue(TDescriptor descriptor); - - public void Dispose() - { - foreach (T value in _cache.Values) - { - value.Dispose(); - } - - GC.SuppressFinalize(this); - } - - public T GetOrCreate(TDescriptor descriptor) - { - THash hash = GetHash(descriptor); - if (_cache.TryGetValue(hash, out T value)) - { - return value; - } - else - { - T newValue = CreateValue(descriptor); - _cache.Add(hash, newValue); - - return newValue; - } - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/StringHelper.cs b/src/Ryujinx.Graphics.Metal/StringHelper.cs deleted file mode 100644 index 46e8ad2e9..000000000 --- a/src/Ryujinx.Graphics.Metal/StringHelper.cs +++ /dev/null @@ -1,30 +0,0 @@ -using SharpMetal.Foundation; -using SharpMetal.ObjectiveCCore; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - class StringHelper - { - public static NSString NSString(string source) - { - return new(ObjectiveC.IntPtr_objc_msgSend(new ObjectiveCClass("NSString"), "stringWithUTF8String:", source)); - } - - public static unsafe string String(NSString source) - { - char[] sourceBuffer = new char[source.Length]; - fixed (char* pSourceBuffer = sourceBuffer) - { - ObjectiveC.bool_objc_msgSend(source, - "getCString:maxLength:encoding:", - pSourceBuffer, - source.MaximumLengthOfBytes(NSStringEncoding.UTF16) + 1, - (ulong)NSStringEncoding.UTF16); - } - - return new string(sourceBuffer); - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/SyncManager.cs b/src/Ryujinx.Graphics.Metal/SyncManager.cs deleted file mode 100644 index f2f26fd91..000000000 --- a/src/Ryujinx.Graphics.Metal/SyncManager.cs +++ /dev/null @@ -1,214 +0,0 @@ -using Ryujinx.Common.Logging; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - class SyncManager - { - private class SyncHandle - { - public ulong ID; - public MultiFenceHolder Waitable; - public ulong FlushId; - public bool Signalled; - - public bool NeedsFlush(ulong currentFlushId) - { - return (long)(FlushId - currentFlushId) >= 0; - } - } - - private ulong _firstHandle; - - private readonly MetalRenderer _renderer; - private readonly List _handles; - private ulong _flushId; - private long _waitTicks; - - public SyncManager(MetalRenderer renderer) - { - _renderer = renderer; - _handles = []; - } - - public void RegisterFlush() - { - _flushId++; - } - - public void Create(ulong id, bool strict) - { - ulong flushId = _flushId; - MultiFenceHolder waitable = new(); - if (strict || _renderer.InterruptAction == null) - { - _renderer.FlushAllCommands(); - _renderer.CommandBufferPool.AddWaitable(waitable); - } - else - { - // Don't flush commands, instead wait for the current command buffer to finish. - // If this sync is waited on before the command buffer is submitted, interrupt the gpu thread and flush it manually. - - _renderer.CommandBufferPool.AddInUseWaitable(waitable); - } - - SyncHandle handle = new() - { - ID = id, - Waitable = waitable, - FlushId = flushId, - }; - - lock (_handles) - { - _handles.Add(handle); - } - } - - public ulong GetCurrent() - { - lock (_handles) - { - ulong lastHandle = _firstHandle; - - foreach (SyncHandle handle in _handles) - { - lock (handle) - { - if (handle.Waitable == null) - { - continue; - } - - if (handle.ID > lastHandle) - { - bool signaled = handle.Signalled || handle.Waitable.WaitForFences(false); - if (signaled) - { - lastHandle = handle.ID; - handle.Signalled = true; - } - } - } - } - - return lastHandle; - } - } - - public void Wait(ulong id) - { - SyncHandle result = null; - - lock (_handles) - { - if ((long)(_firstHandle - id) > 0) - { - return; // The handle has already been signalled or deleted. - } - - foreach (SyncHandle handle in _handles) - { - if (handle.ID == id) - { - result = handle; - break; - } - } - } - - if (result != null) - { - if (result.Waitable == null) - { - return; - } - - long beforeTicks = Stopwatch.GetTimestamp(); - - if (result.NeedsFlush(_flushId)) - { - _renderer.InterruptAction(() => - { - if (result.NeedsFlush(_flushId)) - { - _renderer.FlushAllCommands(); - } - }); - } - - lock (result) - { - if (result.Waitable == null) - { - return; - } - - bool signaled = result.Signalled || result.Waitable.WaitForFences(true); - - if (!signaled) - { - Logger.Error?.PrintMsg(LogClass.Gpu, $"Metal Sync Object {result.ID} failed to signal within 1000ms. Continuing..."); - } - else - { - _waitTicks += Stopwatch.GetTimestamp() - beforeTicks; - result.Signalled = true; - } - } - } - } - - public void Cleanup() - { - // Iterate through handles and remove any that have already been signalled. - - while (true) - { - SyncHandle first = null; - lock (_handles) - { - first = _handles.FirstOrDefault(); - } - - if (first == null || first.NeedsFlush(_flushId)) - { - break; - } - - bool signaled = first.Waitable.WaitForFences(false); - if (signaled) - { - // Delete the sync object. - lock (_handles) - { - lock (first) - { - _firstHandle = first.ID + 1; - _handles.RemoveAt(0); - first.Waitable = null; - } - } - } - else - { - // This sync handle and any following have not been reached yet. - break; - } - } - } - - public long GetAndResetWaitTicks() - { - long result = _waitTicks; - _waitTicks = 0; - - return result; - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs deleted file mode 100644 index 754bf1742..000000000 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ /dev/null @@ -1,654 +0,0 @@ -using Ryujinx.Common.Logging; -using Ryujinx.Common.Memory; -using Ryujinx.Graphics.GAL; -using SharpMetal.Foundation; -using SharpMetal.Metal; -using System; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - class Texture : TextureBase, ITexture - { - private MTLTexture _identitySwizzleHandle; - private readonly bool _identityIsDifferent; - - public Texture(MTLDevice device, MetalRenderer renderer, Pipeline pipeline, TextureCreateInfo info) : base(device, renderer, pipeline, info) - { - MTLPixelFormat pixelFormat = FormatTable.GetFormat(Info.Format); - - MTLTextureDescriptor descriptor = new() - { - PixelFormat = pixelFormat, - Usage = MTLTextureUsage.Unknown, - SampleCount = (ulong)Info.Samples, - TextureType = Info.Target.Convert(), - Width = (ulong)Info.Width, - Height = (ulong)Info.Height, - MipmapLevelCount = (ulong)Info.Levels - }; - - if (info.Target == Target.Texture3D) - { - descriptor.Depth = (ulong)Info.Depth; - } - else if (info.Target != Target.Cubemap) - { - if (info.Target == Target.CubemapArray) - { - descriptor.ArrayLength = (ulong)(Info.Depth / 6); - } - else - { - descriptor.ArrayLength = (ulong)Info.Depth; - } - } - - MTLTextureSwizzleChannels swizzle = GetSwizzle(info, descriptor.PixelFormat); - - _identitySwizzleHandle = Device.NewTexture(descriptor); - - if (SwizzleIsIdentity(swizzle)) - { - MtlTexture = _identitySwizzleHandle; - } - else - { - MtlTexture = CreateDefaultView(_identitySwizzleHandle, swizzle, descriptor); - _identityIsDifferent = true; - } - - MtlFormat = pixelFormat; - descriptor.Dispose(); - } - - public Texture(MTLDevice device, MetalRenderer renderer, Pipeline pipeline, TextureCreateInfo info, MTLTexture sourceTexture, int firstLayer, int firstLevel) : base(device, renderer, pipeline, info) - { - MTLPixelFormat pixelFormat = FormatTable.GetFormat(Info.Format); - - if (info.DepthStencilMode == DepthStencilMode.Stencil) - { - pixelFormat = pixelFormat switch - { - MTLPixelFormat.Depth32FloatStencil8 => MTLPixelFormat.X32Stencil8, - MTLPixelFormat.Depth24UnormStencil8 => MTLPixelFormat.X24Stencil8, - _ => pixelFormat - }; - } - - MTLTextureType textureType = Info.Target.Convert(); - NSRange levels; - levels.location = (ulong)firstLevel; - levels.length = (ulong)Info.Levels; - NSRange slices; - slices.location = (ulong)firstLayer; - slices.length = textureType == MTLTextureType.Type3D ? 1 : (ulong)info.GetDepthOrLayers(); - - MTLTextureSwizzleChannels swizzle = GetSwizzle(info, pixelFormat); - - _identitySwizzleHandle = sourceTexture.NewTextureView(pixelFormat, textureType, levels, slices); - - if (SwizzleIsIdentity(swizzle)) - { - MtlTexture = _identitySwizzleHandle; - } - else - { - MtlTexture = sourceTexture.NewTextureView(pixelFormat, textureType, levels, slices, swizzle); - _identityIsDifferent = true; - } - - MtlFormat = pixelFormat; - FirstLayer = firstLayer; - FirstLevel = firstLevel; - } - - public void PopulateRenderPassAttachment(MTLRenderPassColorAttachmentDescriptor descriptor) - { - descriptor.Texture = _identitySwizzleHandle; - } - - private MTLTexture CreateDefaultView(MTLTexture texture, MTLTextureSwizzleChannels swizzle, MTLTextureDescriptor descriptor) - { - NSRange levels; - levels.location = 0; - levels.length = (ulong)Info.Levels; - NSRange slices; - slices.location = 0; - slices.length = Info.Target == Target.Texture3D ? 1 : (ulong)Info.GetDepthOrLayers(); - - return texture.NewTextureView(descriptor.PixelFormat, descriptor.TextureType, levels, slices, swizzle); - } - - private bool SwizzleIsIdentity(MTLTextureSwizzleChannels swizzle) - { - return swizzle.red == MTLTextureSwizzle.Red && - swizzle.green == MTLTextureSwizzle.Green && - swizzle.blue == MTLTextureSwizzle.Blue && - swizzle.alpha == MTLTextureSwizzle.Alpha; - } - - private MTLTextureSwizzleChannels GetSwizzle(TextureCreateInfo info, MTLPixelFormat pixelFormat) - { - MTLTextureSwizzle swizzleR = Info.SwizzleR.Convert(); - MTLTextureSwizzle swizzleG = Info.SwizzleG.Convert(); - MTLTextureSwizzle swizzleB = Info.SwizzleB.Convert(); - MTLTextureSwizzle swizzleA = Info.SwizzleA.Convert(); - - if (info.Format == Format.R5G5B5A1Unorm || - info.Format == Format.R5G5B5X1Unorm || - info.Format == Format.R5G6B5Unorm) - { - (swizzleB, swizzleR) = (swizzleR, swizzleB); - } - else if (pixelFormat == MTLPixelFormat.ABGR4Unorm || info.Format == Format.A1B5G5R5Unorm) - { - MTLTextureSwizzle tempB = swizzleB; - MTLTextureSwizzle tempA = swizzleA; - - swizzleB = swizzleG; - swizzleA = swizzleR; - swizzleR = tempA; - swizzleG = tempB; - } - - return new MTLTextureSwizzleChannels - { - red = swizzleR, - green = swizzleG, - blue = swizzleB, - alpha = swizzleA - }; - } - - public void CopyTo(ITexture destination, int firstLayer, int firstLevel) - { - CommandBufferScoped cbs = Pipeline.Cbs; - - TextureBase src = this; - TextureBase dst = (TextureBase)destination; - - if (!Valid || !dst.Valid) - { - return; - } - - MTLTexture srcImage = GetHandle(); - MTLTexture dstImage = dst.GetHandle(); - - if (!dst.Info.Target.IsMultisample() && Info.Target.IsMultisample()) - { - // int layers = Math.Min(Info.GetLayers(), dst.Info.GetLayers() - firstLayer); - - // _gd.HelperShader.CopyMSToNonMS(_gd, cbs, src, dst, 0, firstLayer, layers); - } - else if (dst.Info.Target.IsMultisample() && !Info.Target.IsMultisample()) - { - // int layers = Math.Min(Info.GetLayers(), dst.Info.GetLayers() - firstLayer); - - // _gd.HelperShader.CopyNonMSToMS(_gd, cbs, src, dst, 0, firstLayer, layers); - } - else if (dst.Info.BytesPerPixel != Info.BytesPerPixel) - { - // int layers = Math.Min(Info.GetLayers(), dst.Info.GetLayers() - firstLayer); - // int levels = Math.Min(Info.Levels, dst.Info.Levels - firstLevel); - - // _gd.HelperShader.CopyIncompatibleFormats(_gd, cbs, src, dst, 0, firstLayer, 0, firstLevel, layers, levels); - } - else if (src.Info.Format.IsDepthOrStencil() != dst.Info.Format.IsDepthOrStencil()) - { - // int layers = Math.Min(Info.GetLayers(), dst.Info.GetLayers() - firstLayer); - // int levels = Math.Min(Info.Levels, dst.Info.Levels - firstLevel); - - // TODO: depth copy? - // _gd.HelperShader.CopyColor(_gd, cbs, src, dst, 0, firstLayer, 0, FirstLevel, layers, levels); - } - else - { - TextureCopy.Copy( - cbs, - srcImage, - dstImage, - src.Info, - dst.Info, - 0, - firstLayer, - 0, - firstLevel); - } - } - - public void CopyTo(ITexture destination, int srcLayer, int dstLayer, int srcLevel, int dstLevel) - { - CommandBufferScoped cbs = Pipeline.Cbs; - - TextureBase src = this; - TextureBase dst = (TextureBase)destination; - - if (!Valid || !dst.Valid) - { - return; - } - - MTLTexture srcImage = GetHandle(); - MTLTexture dstImage = dst.GetHandle(); - - if (!dst.Info.Target.IsMultisample() && Info.Target.IsMultisample()) - { - // _gd.HelperShader.CopyMSToNonMS(_gd, cbs, src, dst, srcLayer, dstLayer, 1); - } - else if (dst.Info.Target.IsMultisample() && !Info.Target.IsMultisample()) - { - // _gd.HelperShader.CopyNonMSToMS(_gd, cbs, src, dst, srcLayer, dstLayer, 1); - } - else if (dst.Info.BytesPerPixel != Info.BytesPerPixel) - { - // _gd.HelperShader.CopyIncompatibleFormats(_gd, cbs, src, dst, srcLayer, dstLayer, srcLevel, dstLevel, 1, 1); - } - else if (src.Info.Format.IsDepthOrStencil() != dst.Info.Format.IsDepthOrStencil()) - { - // _gd.HelperShader.CopyColor(_gd, cbs, src, dst, srcLayer, dstLayer, srcLevel, dstLevel, 1, 1); - } - else - { - TextureCopy.Copy( - cbs, - srcImage, - dstImage, - src.Info, - dst.Info, - srcLayer, - dstLayer, - srcLevel, - dstLevel, - 1, - 1); - } - } - - public void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter) - { - if (!Renderer.CommandBufferPool.OwnedByCurrentThread) - { - Logger.Warning?.PrintMsg(LogClass.Gpu, "Metal doesn't currently support scaled blit on background thread."); - - return; - } - - Texture dst = (Texture)destination; - - bool isDepthOrStencil = dst.Info.Format.IsDepthOrStencil(); - - Pipeline.Blit(this, dst, srcRegion, dstRegion, isDepthOrStencil, linearFilter); - } - - public void CopyTo(BufferRange range, int layer, int level, int stride) - { - CommandBufferScoped cbs = Pipeline.Cbs; - - int outSize = Info.GetMipSize(level); - int hostSize = GetBufferDataLength(outSize); - - int offset = range.Offset; - - Auto autoBuffer = Renderer.BufferManager.GetBuffer(range.Handle, true); - MTLBuffer mtlBuffer = autoBuffer.Get(cbs, range.Offset, outSize).Value; - - if (PrepareOutputBuffer(cbs, hostSize, mtlBuffer, out MTLBuffer copyToBuffer, out BufferHolder tempCopyHolder)) - { - offset = 0; - } - - CopyFromOrToBuffer(cbs, copyToBuffer, MtlTexture, hostSize, true, layer, level, 1, 1, singleSlice: true, offset, stride); - - if (tempCopyHolder != null) - { - CopyDataToOutputBuffer(cbs, tempCopyHolder, autoBuffer, hostSize, range.Offset); - tempCopyHolder.Dispose(); - } - } - - public ITexture CreateView(TextureCreateInfo info, int firstLayer, int firstLevel) - { - return new Texture(Device, Renderer, Pipeline, info, _identitySwizzleHandle, firstLayer, firstLevel); - } - - private void CopyDataToBuffer(Span storage, ReadOnlySpan input) - { - if (NeedsD24S8Conversion()) - { - FormatConverter.ConvertD24S8ToD32FS8(storage, input); - return; - } - - input.CopyTo(storage); - } - - private ReadOnlySpan GetDataFromBuffer(ReadOnlySpan storage, int size, Span output) - { - if (NeedsD24S8Conversion()) - { - if (output.IsEmpty) - { - output = new byte[GetBufferDataLength(size)]; - } - - FormatConverter.ConvertD32FS8ToD24S8(output, storage); - return output; - } - - return storage; - } - - private bool PrepareOutputBuffer(CommandBufferScoped cbs, int hostSize, MTLBuffer target, out MTLBuffer copyTarget, out BufferHolder copyTargetHolder) - { - if (NeedsD24S8Conversion()) - { - copyTargetHolder = Renderer.BufferManager.Create(hostSize); - copyTarget = copyTargetHolder.GetBuffer().Get(cbs, 0, hostSize).Value; - - return true; - } - - copyTarget = target; - copyTargetHolder = null; - - return false; - } - - private void CopyDataToOutputBuffer(CommandBufferScoped cbs, BufferHolder hostData, Auto copyTarget, int hostSize, int dstOffset) - { - if (NeedsD24S8Conversion()) - { - Renderer.HelperShader.ConvertD32S8ToD24S8(cbs, hostData, copyTarget, hostSize / (2 * sizeof(int)), dstOffset); - } - } - - private bool NeedsD24S8Conversion() - { - return FormatTable.IsD24S8(Info.Format) && MtlFormat == MTLPixelFormat.Depth32FloatStencil8; - } - - public void CopyFromOrToBuffer( - CommandBufferScoped cbs, - MTLBuffer buffer, - MTLTexture image, - int size, - bool to, - int dstLayer, - int dstLevel, - int dstLayers, - int dstLevels, - bool singleSlice, - int offset = 0, - int stride = 0) - { - MTLBlitCommandEncoder blitCommandEncoder = cbs.Encoders.EnsureBlitEncoder(); - - bool is3D = Info.Target == Target.Texture3D; - int width = Math.Max(1, Info.Width >> dstLevel); - int height = Math.Max(1, Info.Height >> dstLevel); - int depth = is3D && !singleSlice ? Math.Max(1, Info.Depth >> dstLevel) : 1; - int layers = dstLayers; - int levels = dstLevels; - - for (int oLevel = 0; oLevel < levels; oLevel++) - { - int level = oLevel + dstLevel; - int mipSize = Info.GetMipSize2D(level); - - int mipSizeLevel = GetBufferDataLength(is3D && !singleSlice - ? Info.GetMipSize(level) - : mipSize * dstLayers); - - int endOffset = offset + mipSizeLevel; - - if ((uint)endOffset > (uint)size) - { - break; - } - - for (int oLayer = 0; oLayer < layers; oLayer++) - { - int layer = !is3D ? dstLayer + oLayer : 0; - int z = is3D ? dstLayer + oLayer : 0; - - if (to) - { - blitCommandEncoder.CopyFromTexture( - image, - (ulong)layer, - (ulong)level, - new MTLOrigin { z = (ulong)z }, - new MTLSize { width = (ulong)width, height = (ulong)height, depth = 1 }, - buffer, - (ulong)offset, - (ulong)Info.GetMipStride(level), - (ulong)mipSize - ); - } - else - { - blitCommandEncoder.CopyFromBuffer( - buffer, - (ulong)offset, - (ulong)Info.GetMipStride(level), - (ulong)mipSize, - new MTLSize { width = (ulong)width, height = (ulong)height, depth = 1 }, - image, - (ulong)(layer + oLayer), - (ulong)level, - new MTLOrigin { z = (ulong)z } - ); - } - - offset += mipSize; - } - - width = Math.Max(1, width >> 1); - height = Math.Max(1, height >> 1); - - if (Info.Target == Target.Texture3D) - { - depth = Math.Max(1, depth >> 1); - } - } - } - - private ReadOnlySpan GetData(CommandBufferPool cbp, PersistentFlushBuffer flushBuffer) - { - int size = 0; - - for (int level = 0; level < Info.Levels; level++) - { - size += Info.GetMipSize(level); - } - - size = GetBufferDataLength(size); - - Span result = flushBuffer.GetTextureData(cbp, this, size); - - return GetDataFromBuffer(result, size, result); - } - - private ReadOnlySpan GetData(CommandBufferPool cbp, PersistentFlushBuffer flushBuffer, int layer, int level) - { - int size = GetBufferDataLength(Info.GetMipSize(level)); - - Span result = flushBuffer.GetTextureData(cbp, this, size, layer, level); - - return GetDataFromBuffer(result, size, result); - } - - public PinnedSpan GetData() - { - BackgroundResource resources = Renderer.BackgroundResources.Get(); - - if (Renderer.CommandBufferPool.OwnedByCurrentThread) - { - Renderer.FlushAllCommands(); - - return PinnedSpan.UnsafeFromSpan(GetData(Renderer.CommandBufferPool, resources.GetFlushBuffer())); - } - - return PinnedSpan.UnsafeFromSpan(GetData(resources.GetPool(), resources.GetFlushBuffer())); - } - - public PinnedSpan GetData(int layer, int level) - { - BackgroundResource resources = Renderer.BackgroundResources.Get(); - - if (Renderer.CommandBufferPool.OwnedByCurrentThread) - { - Renderer.FlushAllCommands(); - - return PinnedSpan.UnsafeFromSpan(GetData(Renderer.CommandBufferPool, resources.GetFlushBuffer(), layer, level)); - } - - return PinnedSpan.UnsafeFromSpan(GetData(resources.GetPool(), resources.GetFlushBuffer(), layer, level)); - } - - public void SetData(MemoryOwner data) - { - MTLBlitCommandEncoder blitCommandEncoder = Pipeline.GetOrCreateBlitEncoder(); - - Span dataSpan = data.Memory.Span; - - BufferHolder buffer = Renderer.BufferManager.Create(dataSpan.Length); - buffer.SetDataUnchecked(0, dataSpan); - MTLBuffer mtlBuffer = buffer.GetBuffer(false).Get(Pipeline.Cbs).Value; - - int width = Info.Width; - int height = Info.Height; - int depth = Info.Depth; - int levels = Info.Levels; - int layers = Info.GetLayers(); - bool is3D = Info.Target == Target.Texture3D; - - int offset = 0; - - for (int level = 0; level < levels; level++) - { - int mipSize = Info.GetMipSize2D(level); - int endOffset = offset + mipSize; - - if ((uint)endOffset > (uint)dataSpan.Length) - { - return; - } - - for (int layer = 0; layer < layers; layer++) - { - blitCommandEncoder.CopyFromBuffer( - mtlBuffer, - (ulong)offset, - (ulong)Info.GetMipStride(level), - (ulong)mipSize, - new MTLSize { width = (ulong)width, height = (ulong)height, depth = is3D ? (ulong)depth : 1 }, - MtlTexture, - (ulong)layer, - (ulong)level, - new MTLOrigin() - ); - - offset += mipSize; - } - - width = Math.Max(1, width >> 1); - height = Math.Max(1, height >> 1); - - if (is3D) - { - depth = Math.Max(1, depth >> 1); - } - } - - // Cleanup - buffer.Dispose(); - } - - private void SetData(ReadOnlySpan data, int layer, int level, int layers, int levels, bool singleSlice) - { - int bufferDataLength = GetBufferDataLength(data.Length); - - using BufferHolder bufferHolder = Renderer.BufferManager.Create(bufferDataLength); - - // TODO: loadInline logic - - CommandBufferScoped cbs = Pipeline.Cbs; - - CopyDataToBuffer(bufferHolder.GetDataStorage(0, bufferDataLength), data); - - MTLBuffer buffer = bufferHolder.GetBuffer().Get(cbs).Value; - MTLTexture image = GetHandle(); - - CopyFromOrToBuffer(cbs, buffer, image, bufferDataLength, false, layer, level, layers, levels, singleSlice); - } - - public void SetData(MemoryOwner data, int layer, int level) - { - SetData(data.Memory.Span, layer, level, 1, 1, singleSlice: true); - - data.Dispose(); - } - - public void SetData(MemoryOwner data, int layer, int level, Rectangle region) - { - MTLBlitCommandEncoder blitCommandEncoder = Pipeline.GetOrCreateBlitEncoder(); - - ulong bytesPerRow = (ulong)Info.GetMipStride(level); - ulong bytesPerImage = 0; - if (MtlTexture.TextureType == MTLTextureType.Type3D) - { - bytesPerImage = bytesPerRow * (ulong)Info.Height; - } - - Span dataSpan = data.Memory.Span; - - BufferHolder buffer = Renderer.BufferManager.Create(dataSpan.Length); - buffer.SetDataUnchecked(0, dataSpan); - MTLBuffer mtlBuffer = buffer.GetBuffer(false).Get(Pipeline.Cbs).Value; - - blitCommandEncoder.CopyFromBuffer( - mtlBuffer, - 0, - bytesPerRow, - bytesPerImage, - new MTLSize { width = (ulong)region.Width, height = (ulong)region.Height, depth = 1 }, - MtlTexture, - (ulong)layer, - (ulong)level, - new MTLOrigin { x = (ulong)region.X, y = (ulong)region.Y } - ); - - // Cleanup - buffer.Dispose(); - } - - private int GetBufferDataLength(int length) - { - if (NeedsD24S8Conversion()) - { - return length * 2; - } - - return length; - } - - public void SetStorage(BufferRange buffer) - { - throw new NotImplementedException(); - } - - public override void Release() - { - if (_identityIsDifferent) - { - _identitySwizzleHandle.Dispose(); - } - - base.Release(); - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/TextureArray.cs b/src/Ryujinx.Graphics.Metal/TextureArray.cs deleted file mode 100644 index ea2c74420..000000000 --- a/src/Ryujinx.Graphics.Metal/TextureArray.cs +++ /dev/null @@ -1,93 +0,0 @@ -using Ryujinx.Graphics.GAL; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - internal class TextureArray : ITextureArray - { - private readonly TextureRef[] _textureRefs; - private readonly TextureBuffer[] _bufferTextureRefs; - - private readonly bool _isBuffer; - private readonly Pipeline _pipeline; - - public TextureArray(int size, bool isBuffer, Pipeline pipeline) - { - if (isBuffer) - { - _bufferTextureRefs = new TextureBuffer[size]; - } - else - { - _textureRefs = new TextureRef[size]; - } - - _isBuffer = isBuffer; - _pipeline = pipeline; - } - - public void SetSamplers(int index, ISampler[] samplers) - { - for (int i = 0; i < samplers.Length; i++) - { - ISampler sampler = samplers[i]; - - if (sampler is SamplerHolder samp) - { - _textureRefs[index + i].Sampler = samp.GetSampler(); - } - else - { - _textureRefs[index + i].Sampler = default; - } - } - - SetDirty(); - } - - public void SetTextures(int index, ITexture[] textures) - { - for (int i = 0; i < textures.Length; i++) - { - ITexture texture = textures[i]; - - if (texture is TextureBuffer textureBuffer) - { - _bufferTextureRefs[index + i] = textureBuffer; - } - else if (texture is Texture tex) - { - _textureRefs[index + i].Storage = tex; - } - else if (!_isBuffer) - { - _textureRefs[index + i].Storage = null; - } - else - { - _bufferTextureRefs[index + i] = null; - } - } - - SetDirty(); - } - - public TextureRef[] GetTextureRefs() - { - return _textureRefs; - } - - public TextureBuffer[] GetBufferTextureRefs() - { - return _bufferTextureRefs; - } - - private void SetDirty() - { - _pipeline.DirtyTextures(); - } - - public void Dispose() { } - } -} diff --git a/src/Ryujinx.Graphics.Metal/TextureBase.cs b/src/Ryujinx.Graphics.Metal/TextureBase.cs deleted file mode 100644 index fecd64958..000000000 --- a/src/Ryujinx.Graphics.Metal/TextureBase.cs +++ /dev/null @@ -1,67 +0,0 @@ -using Ryujinx.Graphics.GAL; -using SharpMetal.Metal; -using System; -using System.Runtime.Versioning; -using System.Threading; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - abstract class TextureBase : IDisposable - { - private int _isValid = 1; - - public bool Valid => Volatile.Read(ref _isValid) != 0; - - protected readonly Pipeline Pipeline; - protected readonly MTLDevice Device; - protected readonly MetalRenderer Renderer; - - protected MTLTexture MtlTexture; - - public readonly TextureCreateInfo Info; - public int Width => Info.Width; - public int Height => Info.Height; - public int Depth => Info.Depth; - - public MTLPixelFormat MtlFormat { get; protected set; } - public int FirstLayer { get; protected set; } - public int FirstLevel { get; protected set; } - - public TextureBase(MTLDevice device, MetalRenderer renderer, Pipeline pipeline, TextureCreateInfo info) - { - Device = device; - Renderer = renderer; - Pipeline = pipeline; - Info = info; - } - - public MTLTexture GetHandle() - { - if (_isValid == 0) - { - return new MTLTexture(IntPtr.Zero); - } - - return MtlTexture; - } - - public virtual void Release() - { - Dispose(); - } - - public void Dispose() - { - bool wasValid = Interlocked.Exchange(ref _isValid, 0) != 0; - - if (wasValid) - { - if (MtlTexture != IntPtr.Zero) - { - MtlTexture.Dispose(); - } - } - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/TextureBuffer.cs b/src/Ryujinx.Graphics.Metal/TextureBuffer.cs deleted file mode 100644 index 483cef28b..000000000 --- a/src/Ryujinx.Graphics.Metal/TextureBuffer.cs +++ /dev/null @@ -1,132 +0,0 @@ -using Ryujinx.Common.Memory; -using Ryujinx.Graphics.GAL; -using SharpMetal.Metal; -using System; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - class TextureBuffer : TextureBase, ITexture - { - private MTLTextureDescriptor _descriptor; - private BufferHandle _bufferHandle; - private int _offset; - private int _size; - - private int _bufferCount; - private Auto _buffer; - - public TextureBuffer(MTLDevice device, MetalRenderer renderer, Pipeline pipeline, TextureCreateInfo info) : base(device, renderer, pipeline, info) - { - MTLPixelFormat pixelFormat = FormatTable.GetFormat(Info.Format); - - _descriptor = new MTLTextureDescriptor - { - PixelFormat = pixelFormat, - Usage = MTLTextureUsage.Unknown, - TextureType = MTLTextureType.TextureBuffer, - Width = (ulong)Info.Width, - Height = (ulong)Info.Height, - }; - - MtlFormat = pixelFormat; - } - - public void RebuildStorage(bool write) - { - if (MtlTexture != IntPtr.Zero) - { - MtlTexture.Dispose(); - } - - if (_buffer == null) - { - MtlTexture = default; - } - else - { - DisposableBuffer buffer = _buffer.Get(Pipeline.Cbs, _offset, _size, write); - - _descriptor.Width = (uint)(_size / Info.BytesPerPixel); - MtlTexture = buffer.Value.NewTexture(_descriptor, (ulong)_offset, (ulong)_size); - } - } - - public void CopyTo(ITexture destination, int firstLayer, int firstLevel) - { - throw new NotSupportedException(); - } - - public void CopyTo(ITexture destination, int srcLayer, int dstLayer, int srcLevel, int dstLevel) - { - throw new NotSupportedException(); - } - - public void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter) - { - throw new NotSupportedException(); - } - - public ITexture CreateView(TextureCreateInfo info, int firstLayer, int firstLevel) - { - throw new NotSupportedException(); - } - - public PinnedSpan GetData() - { - return Renderer.GetBufferData(_bufferHandle, _offset, _size); - } - - public PinnedSpan GetData(int layer, int level) - { - return GetData(); - } - - public void CopyTo(BufferRange range, int layer, int level, int stride) - { - throw new NotImplementedException(); - } - - public void SetData(MemoryOwner data) - { - Renderer.SetBufferData(_bufferHandle, _offset, data.Memory.Span); - data.Dispose(); - } - - public void SetData(MemoryOwner data, int layer, int level) - { - throw new NotSupportedException(); - } - - public void SetData(MemoryOwner data, int layer, int level, Rectangle region) - { - throw new NotSupportedException(); - } - - public void SetStorage(BufferRange buffer) - { - if (_bufferHandle == buffer.Handle && - _offset == buffer.Offset && - _size == buffer.Size && - _bufferCount == Renderer.BufferManager.BufferCount) - { - return; - } - - _bufferHandle = buffer.Handle; - _offset = buffer.Offset; - _size = buffer.Size; - _bufferCount = Renderer.BufferManager.BufferCount; - - _buffer = Renderer.BufferManager.GetBuffer(_bufferHandle, false); - } - - public override void Release() - { - _descriptor.Dispose(); - - base.Release(); - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/TextureCopy.cs b/src/Ryujinx.Graphics.Metal/TextureCopy.cs deleted file mode 100644 index afd3e961f..000000000 --- a/src/Ryujinx.Graphics.Metal/TextureCopy.cs +++ /dev/null @@ -1,265 +0,0 @@ -using Ryujinx.Common; -using Ryujinx.Common.Logging; -using Ryujinx.Graphics.GAL; -using SharpMetal.Metal; -using System; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - static class TextureCopy - { - public static ulong CopyFromOrToBuffer( - CommandBufferScoped cbs, - MTLBuffer buffer, - MTLTexture image, - TextureCreateInfo info, - bool to, - int dstLayer, - int dstLevel, - int x, - int y, - int width, - int height, - ulong offset = 0) - { - MTLBlitCommandEncoder blitCommandEncoder = cbs.Encoders.EnsureBlitEncoder(); - - bool is3D = info.Target == Target.Texture3D; - - int blockWidth = BitUtils.DivRoundUp(width, info.BlockWidth); - int blockHeight = BitUtils.DivRoundUp(height, info.BlockHeight); - ulong bytesPerRow = (ulong)BitUtils.AlignUp(blockWidth * info.BytesPerPixel, 4); - ulong bytesPerImage = bytesPerRow * (ulong)blockHeight; - - MTLOrigin origin = new() { x = (ulong)x, y = (ulong)y, z = is3D ? (ulong)dstLayer : 0 }; - MTLSize region = new() { width = (ulong)width, height = (ulong)height, depth = 1 }; - - uint layer = is3D ? 0 : (uint)dstLayer; - - if (to) - { - blitCommandEncoder.CopyFromTexture( - image, - layer, - (ulong)dstLevel, - origin, - region, - buffer, - offset, - bytesPerRow, - bytesPerImage); - } - else - { - blitCommandEncoder.CopyFromBuffer(buffer, offset, bytesPerRow, bytesPerImage, region, image, layer, (ulong)dstLevel, origin); - } - - return offset + bytesPerImage; - } - - public static void Copy( - CommandBufferScoped cbs, - MTLTexture srcImage, - MTLTexture dstImage, - TextureCreateInfo srcInfo, - TextureCreateInfo dstInfo, - int srcLayer, - int dstLayer, - int srcLevel, - int dstLevel) - { - int srcDepth = srcInfo.GetDepthOrLayers(); - int srcLevels = srcInfo.Levels; - - int dstDepth = dstInfo.GetDepthOrLayers(); - int dstLevels = dstInfo.Levels; - - if (dstInfo.Target == Target.Texture3D) - { - dstDepth = Math.Max(1, dstDepth >> dstLevel); - } - - int depth = Math.Min(srcDepth, dstDepth); - int levels = Math.Min(srcLevels, dstLevels); - - Copy( - cbs, - srcImage, - dstImage, - srcInfo, - dstInfo, - srcLayer, - dstLayer, - srcLevel, - dstLevel, - depth, - levels); - } - - public static void Copy( - CommandBufferScoped cbs, - MTLTexture srcImage, - MTLTexture dstImage, - TextureCreateInfo srcInfo, - TextureCreateInfo dstInfo, - int srcDepthOrLayer, - int dstDepthOrLayer, - int srcLevel, - int dstLevel, - int depthOrLayers, - int levels) - { - MTLBlitCommandEncoder blitCommandEncoder = cbs.Encoders.EnsureBlitEncoder(); - - int srcZ; - int srcLayer; - int srcDepth; - int srcLayers; - - if (srcInfo.Target == Target.Texture3D) - { - srcZ = srcDepthOrLayer; - srcLayer = 0; - srcDepth = depthOrLayers; - srcLayers = 1; - } - else - { - srcZ = 0; - srcLayer = srcDepthOrLayer; - srcDepth = 1; - srcLayers = depthOrLayers; - } - - int dstZ; - int dstLayer; - int dstLayers; - - if (dstInfo.Target == Target.Texture3D) - { - dstZ = dstDepthOrLayer; - dstLayer = 0; - dstLayers = 1; - } - else - { - dstZ = 0; - dstLayer = dstDepthOrLayer; - dstLayers = depthOrLayers; - } - - int srcWidth = srcInfo.Width; - int srcHeight = srcInfo.Height; - - int dstWidth = dstInfo.Width; - int dstHeight = dstInfo.Height; - - srcWidth = Math.Max(1, srcWidth >> srcLevel); - srcHeight = Math.Max(1, srcHeight >> srcLevel); - - dstWidth = Math.Max(1, dstWidth >> dstLevel); - dstHeight = Math.Max(1, dstHeight >> dstLevel); - - int blockWidth = 1; - int blockHeight = 1; - bool sizeInBlocks = false; - - MTLBuffer tempBuffer = default; - - if (srcInfo.Format != dstInfo.Format && (srcInfo.IsCompressed || dstInfo.IsCompressed)) - { - // Compressed alias copies need to happen through a temporary buffer. - // The data is copied from the source to the buffer, then the buffer to the destination. - // The length of the buffer should be the maximum slice size for the destination. - - tempBuffer = blitCommandEncoder.Device.NewBuffer((ulong)dstInfo.GetMipSize2D(0), MTLResourceOptions.ResourceStorageModePrivate); - } - - // When copying from a compressed to a non-compressed format, - // the non-compressed texture will have the size of the texture - // in blocks (not in texels), so we must adjust that size to - // match the size in texels of the compressed texture. - if (!srcInfo.IsCompressed && dstInfo.IsCompressed) - { - srcWidth *= dstInfo.BlockWidth; - srcHeight *= dstInfo.BlockHeight; - blockWidth = dstInfo.BlockWidth; - blockHeight = dstInfo.BlockHeight; - - sizeInBlocks = true; - } - else if (srcInfo.IsCompressed && !dstInfo.IsCompressed) - { - dstWidth *= srcInfo.BlockWidth; - dstHeight *= srcInfo.BlockHeight; - blockWidth = srcInfo.BlockWidth; - blockHeight = srcInfo.BlockHeight; - } - - int width = Math.Min(srcWidth, dstWidth); - int height = Math.Min(srcHeight, dstHeight); - - for (int level = 0; level < levels; level++) - { - // Stop copy if we are already out of the levels range. - if (level >= srcInfo.Levels || dstLevel + level >= dstInfo.Levels) - { - break; - } - - int copyWidth = sizeInBlocks ? BitUtils.DivRoundUp(width, blockWidth) : width; - int copyHeight = sizeInBlocks ? BitUtils.DivRoundUp(height, blockHeight) : height; - - int layers = Math.Max(dstLayers - dstLayer, srcLayers); - - for (int layer = 0; layer < layers; layer++) - { - if (tempBuffer.NativePtr != 0) - { - // Copy through the temp buffer - CopyFromOrToBuffer(cbs, tempBuffer, srcImage, srcInfo, true, srcLayer + layer, srcLevel + level, 0, 0, copyWidth, copyHeight); - - int dstBufferWidth = sizeInBlocks ? copyWidth * blockWidth : BitUtils.DivRoundUp(copyWidth, blockWidth); - int dstBufferHeight = sizeInBlocks ? copyHeight * blockHeight : BitUtils.DivRoundUp(copyHeight, blockHeight); - - CopyFromOrToBuffer(cbs, tempBuffer, dstImage, dstInfo, false, dstLayer + layer, dstLevel + level, 0, 0, dstBufferWidth, dstBufferHeight); - } - else if (srcInfo.Samples > 1 && srcInfo.Samples != dstInfo.Samples) - { - // TODO - - Logger.Warning?.PrintMsg(LogClass.Gpu, "Unsupported mismatching sample count copy"); - } - else - { - blitCommandEncoder.CopyFromTexture( - srcImage, - (ulong)(srcLayer + layer), - (ulong)(srcLevel + level), - new MTLOrigin { z = (ulong)srcZ }, - new MTLSize { width = (ulong)copyWidth, height = (ulong)copyHeight, depth = (ulong)srcDepth }, - dstImage, - (ulong)(dstLayer + layer), - (ulong)(dstLevel + level), - new MTLOrigin { z = (ulong)dstZ }); - } - } - - width = Math.Max(1, width >> 1); - height = Math.Max(1, height >> 1); - - if (srcInfo.Target == Target.Texture3D) - { - srcDepth = Math.Max(1, srcDepth >> 1); - } - } - - if (tempBuffer.NativePtr != 0) - { - tempBuffer.Dispose(); - } - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/VertexBufferState.cs b/src/Ryujinx.Graphics.Metal/VertexBufferState.cs deleted file mode 100644 index 8fb48ef79..000000000 --- a/src/Ryujinx.Graphics.Metal/VertexBufferState.cs +++ /dev/null @@ -1,60 +0,0 @@ -using Ryujinx.Graphics.GAL; -using SharpMetal.Metal; -using System; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - readonly internal struct VertexBufferState - { - public static VertexBufferState Null => new(BufferHandle.Null, 0, 0, 0); - - private readonly BufferHandle _handle; - private readonly int _offset; - private readonly int _size; - - public readonly int Stride; - public readonly int Divisor; - - public VertexBufferState(BufferHandle handle, int offset, int size, int divisor, int stride = 0) - { - _handle = handle; - _offset = offset; - _size = size; - - Stride = stride; - Divisor = divisor; - } - - public (MTLBuffer, int) GetVertexBuffer(BufferManager bufferManager, CommandBufferScoped cbs) - { - Auto autoBuffer = null; - - if (_handle != BufferHandle.Null) - { - // TODO: Handle restride if necessary - - autoBuffer = bufferManager.GetBuffer(_handle, false, out int size); - - // The original stride must be reapplied in case it was rewritten. - // TODO: Handle restride if necessary - - if (_offset >= size) - { - autoBuffer = null; - } - } - - if (autoBuffer != null) - { - int offset = _offset; - MTLBuffer buffer = autoBuffer.Get(cbs, offset, _size).Value; - - return (buffer, offset); - } - - return (new MTLBuffer(IntPtr.Zero), 0); - } - } -} diff --git a/src/Ryujinx.Graphics.Metal/Window.cs b/src/Ryujinx.Graphics.Metal/Window.cs deleted file mode 100644 index 0b6c6c4d2..000000000 --- a/src/Ryujinx.Graphics.Metal/Window.cs +++ /dev/null @@ -1,234 +0,0 @@ -using Ryujinx.Common.Configuration; -using Ryujinx.Common.Logging; -using Ryujinx.Graphics.GAL; -using Ryujinx.Graphics.Metal.Effects; -using SharpMetal.ObjectiveCCore; -using SharpMetal.QuartzCore; -using System; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Metal -{ - [SupportedOSPlatform("macos")] - class Window : IWindow, IDisposable - { - public bool ScreenCaptureRequested { get; set; } - - private readonly MetalRenderer _renderer; - private CAMetalLayer _metalLayer; - - private int _width; - private int _height; - - private int _requestedWidth; - private int _requestedHeight; - - private AntiAliasing _currentAntiAliasing; - private bool _updateEffect; - private IPostProcessingEffect _effect; - private IScalingFilter _scalingFilter; - private bool _isLinear; - - public bool IsVSyncEnabled => _metalLayer.DisplaySyncEnabled; - - // private float _scalingFilterLevel; - private bool _updateScalingFilter; - private ScalingFilter _currentScalingFilter; - // private bool _colorSpacePassthroughEnabled; - - public Window(MetalRenderer renderer, CAMetalLayer metalLayer) - { - _renderer = renderer; - _metalLayer = metalLayer; - } - - private void ResizeIfNeeded() - { - if (_requestedWidth != 0 && _requestedHeight != 0) - { - // TODO: This is actually a CGSize, but there is no overload for that, so fill the first two fields of rect with the size. - NSRect rect = new(_requestedWidth, _requestedHeight, 0, 0); - - ObjectiveC.objc_msgSend(_metalLayer, "setDrawableSize:", rect); - - _requestedWidth = 0; - _requestedHeight = 0; - } - } - - public void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback) - { - if (_renderer.Pipeline is Pipeline pipeline && texture is Texture tex) - { - ResizeIfNeeded(); - - CAMetalDrawable drawable = new(ObjectiveC.IntPtr_objc_msgSend(_metalLayer, "nextDrawable")); - - _width = (int)drawable.Texture.Width; - _height = (int)drawable.Texture.Height; - - UpdateEffect(); - - if (_effect != null) - { - // TODO: Run Effects - // view = _effect.Run() - } - - int srcX0, srcX1, srcY0, srcY1; - - if (crop.Left == 0 && crop.Right == 0) - { - srcX0 = 0; - srcX1 = tex.Width; - } - else - { - srcX0 = crop.Left; - srcX1 = crop.Right; - } - - if (crop.Top == 0 && crop.Bottom == 0) - { - srcY0 = 0; - srcY1 = tex.Height; - } - else - { - srcY0 = crop.Top; - srcY1 = crop.Bottom; - } - - if (ScreenCaptureRequested) - { - // TODO: Support screen captures - - ScreenCaptureRequested = false; - } - - float ratioX = crop.IsStretched ? 1.0f : MathF.Min(1.0f, _height * crop.AspectRatioX / (_width * crop.AspectRatioY)); - float ratioY = crop.IsStretched ? 1.0f : MathF.Min(1.0f, _width * crop.AspectRatioY / (_height * crop.AspectRatioX)); - - int dstWidth = (int)(_width * ratioX); - int dstHeight = (int)(_height * ratioY); - - int dstPaddingX = (_width - dstWidth) / 2; - int dstPaddingY = (_height - dstHeight) / 2; - - int dstX0 = crop.FlipX ? _width - dstPaddingX : dstPaddingX; - int dstX1 = crop.FlipX ? dstPaddingX : _width - dstPaddingX; - - int dstY0 = crop.FlipY ? _height - dstPaddingY : dstPaddingY; - int dstY1 = crop.FlipY ? dstPaddingY : _height - dstPaddingY; - - if (_scalingFilter != null) - { - // TODO: Run scaling filter - } - - pipeline.Present( - drawable, - tex, - new Extents2D(srcX0, srcY0, srcX1, srcY1), - new Extents2D(dstX0, dstY0, dstX1, dstY1), - _isLinear); - } - } - - public void SetSize(int width, int height) - { - _requestedWidth = width; - _requestedHeight = height; - } - - public void ChangeVSyncMode(VSyncMode vSyncMode) - { - _metalLayer.DisplaySyncEnabled = vSyncMode is VSyncMode.Switch; - } - - public void SetAntiAliasing(AntiAliasing effect) - { - if (_currentAntiAliasing == effect && _effect != null) - { - return; - } - - _currentAntiAliasing = effect; - - _updateEffect = true; - } - - public void SetScalingFilter(ScalingFilter type) - { - if (_currentScalingFilter == type && _effect != null) - { - return; - } - - _currentScalingFilter = type; - - _updateScalingFilter = true; - } - - public void SetScalingFilterLevel(float level) - { - // _scalingFilterLevel = level; - _updateScalingFilter = true; - } - - public void SetColorSpacePassthrough(bool colorSpacePassThroughEnabled) - { - // _colorSpacePassthroughEnabled = colorSpacePassThroughEnabled; - } - - private void UpdateEffect() - { - if (_updateEffect) - { - _updateEffect = false; - - switch (_currentAntiAliasing) - { - case AntiAliasing.Fxaa: - _effect?.Dispose(); - Logger.Warning?.PrintMsg(LogClass.Gpu, "FXAA not implemented for Metal backend!"); - break; - case AntiAliasing.None: - _effect?.Dispose(); - _effect = null; - break; - case AntiAliasing.SmaaLow: - case AntiAliasing.SmaaMedium: - case AntiAliasing.SmaaHigh: - case AntiAliasing.SmaaUltra: - // var quality = _currentAntiAliasing - AntiAliasing.SmaaLow; - Logger.Warning?.PrintMsg(LogClass.Gpu, "SMAA not implemented for Metal backend!"); - break; - } - } - - if (_updateScalingFilter) - { - _updateScalingFilter = false; - - switch (_currentScalingFilter) - { - case ScalingFilter.Bilinear: - case ScalingFilter.Nearest: - _scalingFilter?.Dispose(); - _scalingFilter = null; - _isLinear = _currentScalingFilter == ScalingFilter.Bilinear; - break; - case ScalingFilter.Fsr: - Logger.Warning?.PrintMsg(LogClass.Gpu, "FSR not implemented for Metal backend!"); - break; - } - } - } - - public void Dispose() - { - _metalLayer.Dispose(); - } - } -} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/CodeGenContext.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/CodeGenContext.cs deleted file mode 100644 index 2fa188ea9..000000000 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/CodeGenContext.cs +++ /dev/null @@ -1,108 +0,0 @@ -using Ryujinx.Graphics.Shader.StructuredIr; -using Ryujinx.Graphics.Shader.Translation; -using System.Text; - -namespace Ryujinx.Graphics.Shader.CodeGen.Msl -{ - class CodeGenContext - { - public const string Tab = " "; - - // The number of additional arguments that every function (except for the main one) must have (for instance support_buffer) - public const int AdditionalArgCount = 2; - - public StructuredFunction CurrentFunction { get; set; } - - public StructuredProgramInfo Info { get; } - - public AttributeUsage AttributeUsage { get; } - public ShaderDefinitions Definitions { get; } - public ShaderProperties Properties { get; } - public HostCapabilities HostCapabilities { get; } - public ILogger Logger { get; } - public TargetApi TargetApi { get; } - - public OperandManager OperandManager { get; } - - private readonly StringBuilder _sb; - - private int _level; - - private string _indentation; - - public CodeGenContext(StructuredProgramInfo info, CodeGenParameters parameters) - { - Info = info; - AttributeUsage = parameters.AttributeUsage; - Definitions = parameters.Definitions; - Properties = parameters.Properties; - HostCapabilities = parameters.HostCapabilities; - Logger = parameters.Logger; - TargetApi = parameters.TargetApi; - - OperandManager = new OperandManager(); - - _sb = new StringBuilder(); - } - - public void AppendLine() - { - _sb.AppendLine(); - } - - public void AppendLine(string str) - { - _sb.AppendLine(_indentation + str); - } - - public string GetCode() - { - return _sb.ToString(); - } - - public void EnterScope(string prefix = "") - { - AppendLine(prefix + "{"); - - _level++; - - UpdateIndentation(); - } - - public void LeaveScope(string suffix = "") - { - if (_level == 0) - { - return; - } - - _level--; - - UpdateIndentation(); - - AppendLine("}" + suffix); - } - - public StructuredFunction GetFunction(int id) - { - return Info.Functions[id]; - } - - private void UpdateIndentation() - { - _indentation = GetIndentation(_level); - } - - private static string GetIndentation(int level) - { - string indentation = string.Empty; - - for (int index = 0; index < level; index++) - { - indentation += Tab; - } - - return indentation; - } - } -} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs deleted file mode 100644 index a9464834a..000000000 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs +++ /dev/null @@ -1,577 +0,0 @@ -using Ryujinx.Common; -using Ryujinx.Graphics.Shader.IntermediateRepresentation; -using Ryujinx.Graphics.Shader.StructuredIr; -using Ryujinx.Graphics.Shader.Translation; -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Ryujinx.Graphics.Shader.CodeGen.Msl -{ - static class Declarations - { - /* - * Description of MSL Binding Model - * - * There are a few fundamental differences between how GLSL and MSL handle I/O. - * This comment will set out to describe the reasons why things are done certain ways - * and to describe the overall binding model that we're striving for here. - * - * Main I/O Structs - * - * Each stage has a main input and output struct (if applicable) labeled as [Stage][In/Out], i.e VertexIn. - * Every field within these structs is labeled with an [[attribute(n)]] property, - * and the overall struct is labeled with [[stage_in]] for input structs, and defined as the - * output type of the main shader function for the output struct. This struct also contains special - * attribute-based properties like [[position]] that would be "built-ins" in a GLSL context. - * - * These structs are passed as inputs to all inline functions due to containing "built-ins" - * that inline functions assume access to. - * - * Vertex & Zero Buffers - * - * Binding indices 0-16 are reserved for vertex buffers, and binding 18 is reserved for the zero buffer. - * - * Uniforms & Storage Buffers - * - * Uniforms and storage buffers are tightly packed into their respective argument buffers - * (effectively ignoring binding indices at shader level), with each pointer to the corresponding - * struct that defines the layout and fields of these buffers (usually just a single data array), laid - * out one after the other in ascending order of their binding index. - * - * The uniforms argument buffer is always bound at a fixed index of 20. - * The storage buffers argument buffer is always bound at a fixed index of 21. - * - * These structs are passed as inputs to all inline functions as in GLSL or SPIRV, - * uniforms and storage buffers would be globals, and inline functions assume access to these buffers. - * - * Samplers & Textures - * - * Metal does not have a combined image sampler like sampler2D in GLSL, as a result we need to bind - * an individual texture and a sampler object for each instance of a combined image sampler. - * Samplers and textures are bound in a shared argument buffer. This argument buffer is tightly packed - * (effectively ignoring binding indices at shader level), with texture and their samplers (if present) - * laid out one after the other in ascending order of their binding index. - * - * The samplers and textures argument buffer is always bound at a fixed index of 22. - * - */ - - public static int[] Declare(CodeGenContext context, StructuredProgramInfo info) - { - // TODO: Re-enable this warning - context.AppendLine("#pragma clang diagnostic ignored \"-Wunused-variable\""); - context.AppendLine(); - context.AppendLine("#include "); - context.AppendLine("#include "); - context.AppendLine(); - context.AppendLine("using namespace metal;"); - context.AppendLine(); - - bool fsi = (info.HelperFunctionsMask & HelperFunctionsMask.FSI) != 0; - - DeclareInputAttributes(context, info.IoDefinitions.Where(x => IsUserDefined(x, StorageKind.Input))); - context.AppendLine(); - DeclareOutputAttributes(context, info.IoDefinitions.Where(x => x.StorageKind == StorageKind.Output)); - context.AppendLine(); - DeclareBufferStructures(context, context.Properties.ConstantBuffers.Values.OrderBy(x => x.Binding).ToArray(), true, fsi); - DeclareBufferStructures(context, context.Properties.StorageBuffers.Values.OrderBy(x => x.Binding).ToArray(), false, fsi); - - // We need to declare each set as a new struct - Dictionary textureDefinitions = context.Properties.Textures.Values - .GroupBy(x => x.Set) - .ToDictionary(x => x.Key, x => x.OrderBy(y => y.Binding).ToArray()); - - Dictionary imageDefinitions = context.Properties.Images.Values - .GroupBy(x => x.Set) - .ToDictionary(x => x.Key, x => x.OrderBy(y => y.Binding).ToArray()); - - int[] textureSets = textureDefinitions.Keys.ToArray(); - int[] imageSets = imageDefinitions.Keys.ToArray(); - - int[] sets = textureSets.Union(imageSets).ToArray(); - - foreach (KeyValuePair set in textureDefinitions) - { - DeclareTextures(context, set.Value, set.Key); - } - - foreach (KeyValuePair set in imageDefinitions) - { - DeclareImages(context, set.Value, set.Key, fsi); - } - - if ((info.HelperFunctionsMask & HelperFunctionsMask.FindLSB) != 0) - { - AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/FindLSB.metal"); - } - - if ((info.HelperFunctionsMask & HelperFunctionsMask.FindMSBS32) != 0) - { - AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/FindMSBS32.metal"); - } - - if ((info.HelperFunctionsMask & HelperFunctionsMask.FindMSBU32) != 0) - { - AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/FindMSBU32.metal"); - } - - if ((info.HelperFunctionsMask & HelperFunctionsMask.SwizzleAdd) != 0) - { - AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/SwizzleAdd.metal"); - } - - if ((info.HelperFunctionsMask & HelperFunctionsMask.Precise) != 0) - { - AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/Precise.metal"); - } - - return sets; - } - - static bool IsUserDefined(IoDefinition ioDefinition, StorageKind storageKind) - { - return ioDefinition.StorageKind == storageKind && ioDefinition.IoVariable == IoVariable.UserDefined; - } - - public static void DeclareLocals(CodeGenContext context, StructuredFunction function, ShaderStage stage, bool isMainFunc = false) - { - if (isMainFunc) - { - // TODO: Support OaIndexing - if (context.Definitions.IaIndexing) - { - context.EnterScope($"array {Defaults.IAttributePrefix} = "); - - for (int i = 0; i < Constants.MaxAttributes; i++) - { - context.AppendLine($"in.{Defaults.IAttributePrefix}{i},"); - } - - context.LeaveScope(";"); - } - - DeclareMemories(context, context.Properties.LocalMemories.Values, isShared: false); - DeclareMemories(context, context.Properties.SharedMemories.Values, isShared: true); - - switch (stage) - { - case ShaderStage.Vertex: - context.AppendLine("VertexOut out = {};"); - // TODO: Only add if necessary - context.AppendLine("uint instance_index = instance_id + base_instance;"); - break; - case ShaderStage.Fragment: - context.AppendLine("FragmentOut out = {};"); - break; - } - - // TODO: Only add if necessary - if (stage != ShaderStage.Compute) - { - // MSL does not give us access to [[thread_index_in_simdgroup]] - // outside compute. But we may still need to provide this value in frag/vert. - context.AppendLine("uint thread_index_in_simdgroup = simd_prefix_exclusive_sum(1);"); - } - } - - foreach (AstOperand decl in function.Locals) - { - string name = context.OperandManager.DeclareLocal(decl); - - context.AppendLine(GetVarTypeName(decl.VarType) + " " + name + ";"); - } - } - - public static string GetVarTypeName(AggregateType type, bool atomic = false) - { - string s32 = atomic ? "atomic_int" : "int"; - string u32 = atomic ? "atomic_uint" : "uint"; - - return type switch - { - AggregateType.Void => "void", - AggregateType.Bool => "bool", - AggregateType.FP32 => "float", - AggregateType.S32 => s32, - AggregateType.U32 => u32, - AggregateType.Vector2 | AggregateType.Bool => "bool2", - AggregateType.Vector2 | AggregateType.FP32 => "float2", - AggregateType.Vector2 | AggregateType.S32 => "int2", - AggregateType.Vector2 | AggregateType.U32 => "uint2", - AggregateType.Vector3 | AggregateType.Bool => "bool3", - AggregateType.Vector3 | AggregateType.FP32 => "float3", - AggregateType.Vector3 | AggregateType.S32 => "int3", - AggregateType.Vector3 | AggregateType.U32 => "uint3", - AggregateType.Vector4 | AggregateType.Bool => "bool4", - AggregateType.Vector4 | AggregateType.FP32 => "float4", - AggregateType.Vector4 | AggregateType.S32 => "int4", - AggregateType.Vector4 | AggregateType.U32 => "uint4", - _ => throw new ArgumentException($"Invalid variable type \"{type}\"."), - }; - } - - private static void DeclareMemories(CodeGenContext context, IEnumerable memories, bool isShared) - { - string prefix = isShared ? "threadgroup " : string.Empty; - - foreach (MemoryDefinition memory in memories) - { - string arraySize = string.Empty; - if ((memory.Type & AggregateType.Array) != 0) - { - arraySize = $"[{memory.ArrayLength}]"; - } - string typeName = GetVarTypeName(memory.Type & ~AggregateType.Array); - context.AppendLine($"{prefix}{typeName} {memory.Name}{arraySize};"); - } - } - - private static void DeclareBufferStructures(CodeGenContext context, BufferDefinition[] buffers, bool constant, bool fsi) - { - string name = constant ? "ConstantBuffers" : "StorageBuffers"; - string addressSpace = constant ? "constant" : "device"; - - string[] bufferDec = new string[buffers.Length]; - - for (int i = 0; i < buffers.Length; i++) - { - BufferDefinition buffer = buffers[i]; - - bool needsPadding = buffer.Layout == BufferLayout.Std140; - string fsiSuffix = !constant && fsi ? " [[raster_order_group(0)]]" : string.Empty; - - bufferDec[i] = $"{addressSpace} {Defaults.StructPrefix}_{buffer.Name}* {buffer.Name}{fsiSuffix};"; - - context.AppendLine($"struct {Defaults.StructPrefix}_{buffer.Name}"); - context.EnterScope(); - - foreach (StructureField field in buffer.Type.Fields) - { - AggregateType type = field.Type; - type |= (needsPadding && (field.Type & AggregateType.Array) != 0) - ? AggregateType.Vector4 - : AggregateType.Invalid; - - type &= ~AggregateType.Array; - - string typeName = GetVarTypeName(type); - string arraySuffix = string.Empty; - - if (field.Type.HasFlag(AggregateType.Array)) - { - if (field.ArrayLength > 0) - { - arraySuffix = $"[{field.ArrayLength}]"; - } - else - { - // Probably UB, but this is the approach that MVK takes - arraySuffix = "[1]"; - } - } - - context.AppendLine($"{typeName} {field.Name}{arraySuffix};"); - } - - context.LeaveScope(";"); - context.AppendLine(); - } - - context.AppendLine($"struct {name}"); - context.EnterScope(); - - foreach (string declaration in bufferDec) - { - context.AppendLine(declaration); - } - - context.LeaveScope(";"); - context.AppendLine(); - } - - private static void DeclareTextures(CodeGenContext context, TextureDefinition[] textures, int set) - { - string setName = GetNameForSet(set); - context.AppendLine($"struct {setName}"); - context.EnterScope(); - - List textureDec = []; - - foreach (TextureDefinition texture in textures) - { - if (texture.Type != SamplerType.None) - { - string textureTypeName = texture.Type.ToMslTextureType(texture.Format.GetComponentType()); - - if (texture.ArrayLength > 1) - { - textureTypeName = $"array<{textureTypeName}, {texture.ArrayLength}>"; - } - - textureDec.Add($"{textureTypeName} tex_{texture.Name};"); - } - - if (!texture.Separate && texture.Type != SamplerType.TextureBuffer) - { - string samplerType = "sampler"; - - if (texture.ArrayLength > 1) - { - samplerType = $"array<{samplerType}, {texture.ArrayLength}>"; - } - - textureDec.Add($"{samplerType} samp_{texture.Name};"); - } - } - - foreach (string declaration in textureDec) - { - context.AppendLine(declaration); - } - - context.LeaveScope(";"); - context.AppendLine(); - } - - private static void DeclareImages(CodeGenContext context, TextureDefinition[] images, int set, bool fsi) - { - string setName = GetNameForSet(set); - context.AppendLine($"struct {setName}"); - context.EnterScope(); - - string[] imageDec = new string[images.Length]; - - for (int i = 0; i < images.Length; i++) - { - TextureDefinition image = images[i]; - - string imageTypeName = image.Type.ToMslTextureType(image.Format.GetComponentType(), true); - if (image.ArrayLength > 1) - { - imageTypeName = $"array<{imageTypeName}, {image.ArrayLength}>"; - } - - string fsiSuffix = fsi ? " [[raster_order_group(0)]]" : string.Empty; - - imageDec[i] = $"{imageTypeName} {image.Name}{fsiSuffix};"; - } - - foreach (string declaration in imageDec) - { - context.AppendLine(declaration); - } - - context.LeaveScope(";"); - context.AppendLine(); - } - - private static void DeclareInputAttributes(CodeGenContext context, IEnumerable inputs) - { - if (context.Definitions.Stage == ShaderStage.Compute) - { - return; - } - - switch (context.Definitions.Stage) - { - case ShaderStage.Vertex: - context.AppendLine("struct VertexIn"); - break; - case ShaderStage.Fragment: - context.AppendLine("struct FragmentIn"); - break; - } - - context.EnterScope(); - - if (context.Definitions.Stage == ShaderStage.Fragment) - { - // TODO: check if it's needed - context.AppendLine("float4 position [[position, invariant]];"); - context.AppendLine("bool front_facing [[front_facing]];"); - context.AppendLine("float2 point_coord [[point_coord]];"); - context.AppendLine("uint primitive_id [[primitive_id]];"); - } - - if (context.Definitions.IaIndexing) - { - // MSL does not support arrays in stage I/O - // We need to use the SPIRV-Cross workaround - for (int i = 0; i < Constants.MaxAttributes; i++) - { - string suffix = context.Definitions.Stage == ShaderStage.Fragment ? $"[[user(loc{i})]]" : $"[[attribute({i})]]"; - context.AppendLine($"float4 {Defaults.IAttributePrefix}{i} {suffix};"); - } - } - - if (inputs.Any()) - { - foreach (IoDefinition ioDefinition in inputs.OrderBy(x => x.Location)) - { - if (context.Definitions.IaIndexing && ioDefinition.IoVariable == IoVariable.UserDefined) - { - continue; - } - - string iq = string.Empty; - - if (context.Definitions.Stage == ShaderStage.Fragment) - { - iq = context.Definitions.ImapTypes[ioDefinition.Location].GetFirstUsedType() switch - { - PixelImap.Constant => "[[flat]] ", - PixelImap.ScreenLinear => "[[center_no_perspective]] ", - _ => string.Empty, - }; - } - - string type = ioDefinition.IoVariable switch - { - // IoVariable.Position => "float4", - IoVariable.GlobalId => "uint3", - IoVariable.VertexId => "uint", - IoVariable.VertexIndex => "uint", - // IoVariable.PointCoord => "float2", - _ => GetVarTypeName(context.Definitions.GetUserDefinedType(ioDefinition.Location, isOutput: false)) - }; - string name = ioDefinition.IoVariable switch - { - // IoVariable.Position => "position", - IoVariable.GlobalId => "global_id", - IoVariable.VertexId => "vertex_id", - IoVariable.VertexIndex => "vertex_index", - // IoVariable.PointCoord => "point_coord", - _ => $"{Defaults.IAttributePrefix}{ioDefinition.Location}" - }; - string suffix = ioDefinition.IoVariable switch - { - // IoVariable.Position => "[[position, invariant]]", - IoVariable.GlobalId => "[[thread_position_in_grid]]", - IoVariable.VertexId => "[[vertex_id]]", - // TODO: Avoid potential redeclaration - IoVariable.VertexIndex => "[[vertex_id]]", - // IoVariable.PointCoord => "[[point_coord]]", - IoVariable.UserDefined => context.Definitions.Stage == ShaderStage.Fragment ? $"[[user(loc{ioDefinition.Location})]]" : $"[[attribute({ioDefinition.Location})]]", - _ => string.Empty - }; - - context.AppendLine($"{type} {name} {iq}{suffix};"); - } - } - - context.LeaveScope(";"); - } - - private static void DeclareOutputAttributes(CodeGenContext context, IEnumerable outputs) - { - switch (context.Definitions.Stage) - { - case ShaderStage.Vertex: - context.AppendLine("struct VertexOut"); - break; - case ShaderStage.Fragment: - context.AppendLine("struct FragmentOut"); - break; - case ShaderStage.Compute: - context.AppendLine("struct KernelOut"); - break; - } - - context.EnterScope(); - - if (context.Definitions.OaIndexing) - { - // MSL does not support arrays in stage I/O - // We need to use the SPIRV-Cross workaround - for (int i = 0; i < Constants.MaxAttributes; i++) - { - context.AppendLine($"float4 {Defaults.OAttributePrefix}{i} [[user(loc{i})]];"); - } - } - - if (outputs.Any()) - { - outputs = outputs.OrderBy(x => x.Location); - - if (context.Definitions.Stage == ShaderStage.Fragment && context.Definitions.DualSourceBlend) - { - IoDefinition firstOutput = outputs.ElementAtOrDefault(0); - IoDefinition secondOutput = outputs.ElementAtOrDefault(1); - - string type1 = GetVarTypeName(context.Definitions.GetFragmentOutputColorType(firstOutput.Location)); - string type2 = GetVarTypeName(context.Definitions.GetFragmentOutputColorType(secondOutput.Location)); - - string name1 = $"color{firstOutput.Location}"; - string name2 = $"color{firstOutput.Location + 1}"; - - context.AppendLine($"{type1} {name1} [[color({firstOutput.Location}), index(0)]];"); - context.AppendLine($"{type2} {name2} [[color({firstOutput.Location}), index(1)]];"); - - outputs = outputs.Skip(2); - } - - foreach (IoDefinition ioDefinition in outputs) - { - if (context.Definitions.OaIndexing && ioDefinition.IoVariable == IoVariable.UserDefined) - { - continue; - } - - string type = ioDefinition.IoVariable switch - { - IoVariable.Position => "float4", - IoVariable.PointSize => "float", - IoVariable.FragmentOutputColor => GetVarTypeName(context.Definitions.GetFragmentOutputColorType(ioDefinition.Location)), - IoVariable.FragmentOutputDepth => "float", - IoVariable.ClipDistance => "float", - _ => GetVarTypeName(context.Definitions.GetUserDefinedType(ioDefinition.Location, isOutput: true)) - }; - string name = ioDefinition.IoVariable switch - { - IoVariable.Position => "position", - IoVariable.PointSize => "point_size", - IoVariable.FragmentOutputColor => $"color{ioDefinition.Location}", - IoVariable.FragmentOutputDepth => "depth", - IoVariable.ClipDistance => "clip_distance", - _ => $"{Defaults.OAttributePrefix}{ioDefinition.Location}" - }; - string suffix = ioDefinition.IoVariable switch - { - IoVariable.Position => "[[position, invariant]]", - IoVariable.PointSize => "[[point_size]]", - IoVariable.UserDefined => $"[[user(loc{ioDefinition.Location})]]", - IoVariable.FragmentOutputColor => $"[[color({ioDefinition.Location})]]", - IoVariable.FragmentOutputDepth => "[[depth(any)]]", - IoVariable.ClipDistance => $"[[clip_distance]][{Defaults.TotalClipDistances}]", - _ => string.Empty - }; - - context.AppendLine($"{type} {name} {suffix};"); - } - } - - context.LeaveScope(";"); - } - - private static void AppendHelperFunction(CodeGenContext context, string filename) - { - string code = EmbeddedResources.ReadAllText(filename); - - code = code.Replace("\t", CodeGenContext.Tab); - - context.AppendLine(code); - context.AppendLine(); - } - - public static string GetNameForSet(int set, bool forVar = false) - { - return (uint)set switch - { - Defaults.TexturesSetIndex => forVar ? "textures" : "Textures", - Defaults.ImagesSetIndex => forVar ? "images" : "Images", - _ => $"{(forVar ? "set" : "Set")}{set}" - }; - } - } -} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Defaults.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Defaults.cs deleted file mode 100644 index 511a2f606..000000000 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Defaults.cs +++ /dev/null @@ -1,34 +0,0 @@ -namespace Ryujinx.Graphics.Shader.CodeGen.Msl -{ - static class Defaults - { - public const string LocalNamePrefix = "temp"; - - public const string PerPatchAttributePrefix = "patchAttr"; - public const string IAttributePrefix = "inAttr"; - public const string OAttributePrefix = "outAttr"; - - public const string StructPrefix = "struct"; - - public const string ArgumentNamePrefix = "a"; - - public const string UndefinedName = "0"; - - public const int MaxVertexBuffers = 16; - - public const uint ZeroBufferIndex = MaxVertexBuffers; - public const uint BaseSetIndex = MaxVertexBuffers + 1; - - public const uint ConstantBuffersIndex = BaseSetIndex; - public const uint StorageBuffersIndex = BaseSetIndex + 1; - public const uint TexturesIndex = BaseSetIndex + 2; - public const uint ImagesIndex = BaseSetIndex + 3; - - public const uint ConstantBuffersSetIndex = 0; - public const uint StorageBuffersSetIndex = 1; - public const uint TexturesSetIndex = 2; - public const uint ImagesSetIndex = 3; - - public const int TotalClipDistances = 8; - } -} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/FindLSB.metal b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/FindLSB.metal deleted file mode 100644 index ad786adb3..000000000 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/FindLSB.metal +++ /dev/null @@ -1,5 +0,0 @@ -template -inline T findLSB(T x) -{ - return select(ctz(x), T(-1), x == T(0)); -} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/FindMSBS32.metal b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/FindMSBS32.metal deleted file mode 100644 index af4eb6cbd..000000000 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/FindMSBS32.metal +++ /dev/null @@ -1,5 +0,0 @@ -template -inline T findMSBS32(T x) -{ - return select(clz(T(0)) - (clz(x) + T(1)), T(-1), x == T(0)); -} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/FindMSBU32.metal b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/FindMSBU32.metal deleted file mode 100644 index 6d97c41a9..000000000 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/FindMSBU32.metal +++ /dev/null @@ -1,6 +0,0 @@ -template -inline T findMSBU32(T x) -{ - T v = select(x, T(-1) - x, x < T(0)); - return select(clz(T(0)) - (clz(v) + T(1)), T(-1), v == T(0)); -} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/HelperFunctionNames.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/HelperFunctionNames.cs deleted file mode 100644 index 370159a0e..000000000 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/HelperFunctionNames.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Ryujinx.Graphics.Shader.CodeGen.Msl -{ - static class HelperFunctionNames - { - public static string FindLSB = "findLSB"; - public static string FindMSBS32 = "findMSBS32"; - public static string FindMSBU32 = "findMSBU32"; - public static string SwizzleAdd = "swizzleAdd"; - } -} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/Precise.metal b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/Precise.metal deleted file mode 100644 index 366bea1ac..000000000 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/Precise.metal +++ /dev/null @@ -1,14 +0,0 @@ -template -[[clang::optnone]] T PreciseFAdd(T l, T r) { - return fma(T(1), l, r); -} - -template -[[clang::optnone]] T PreciseFSub(T l, T r) { - return fma(T(-1), r, l); -} - -template -[[clang::optnone]] T PreciseFMul(T l, T r) { - return fma(l, r, T(0)); -} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/SwizzleAdd.metal b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/SwizzleAdd.metal deleted file mode 100644 index 22a079b01..000000000 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/SwizzleAdd.metal +++ /dev/null @@ -1,7 +0,0 @@ -float swizzleAdd(float x, float y, int mask, uint thread_index_in_simdgroup) -{ - float4 xLut = float4(1.0, -1.0, 1.0, 0.0); - float4 yLut = float4(1.0, 1.0, -1.0, 1.0); - int lutIdx = (mask >> (int(thread_index_in_simdgroup & 3u) * 2)) & 3; - return x * xLut[lutIdx] + y * yLut[lutIdx]; -} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs deleted file mode 100644 index 0be6035b6..000000000 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs +++ /dev/null @@ -1,186 +0,0 @@ -using Ryujinx.Graphics.Shader.IntermediateRepresentation; -using Ryujinx.Graphics.Shader.StructuredIr; -using Ryujinx.Graphics.Shader.Translation; -using System; -using System.Text; -using static Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions.InstGenBallot; -using static Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions.InstGenBarrier; -using static Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions.InstGenCall; -using static Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions.InstGenHelper; -using static Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions.InstGenMemory; -using static Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions.InstGenVector; -using static Ryujinx.Graphics.Shader.StructuredIr.InstructionInfo; - -namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions -{ - static class InstGen - { - public static string GetExpression(CodeGenContext context, IAstNode node) - { - if (node is AstOperation operation) - { - return GetExpression(context, operation); - } - else if (node is AstOperand operand) - { - return context.OperandManager.GetExpression(context, operand); - } - - throw new ArgumentException($"Invalid node type \"{node?.GetType().Name ?? "null"}\"."); - } - - private static string GetExpression(CodeGenContext context, AstOperation operation) - { - Instruction inst = operation.Inst; - - InstInfo info = GetInstructionInfo(inst); - - if ((info.Type & InstType.Call) != 0) - { - bool atomic = (info.Type & InstType.Atomic) != 0; - - int arity = (int)(info.Type & InstType.ArityMask); - - StringBuilder builder = new(); - - if (atomic && (operation.StorageKind == StorageKind.StorageBuffer || operation.StorageKind == StorageKind.SharedMemory)) - { - AggregateType dstType = operation.Inst == Instruction.AtomicMaxS32 || operation.Inst == Instruction.AtomicMinS32 - ? AggregateType.S32 - : AggregateType.U32; - - bool shared = operation.StorageKind == StorageKind.SharedMemory; - - builder.Append($"({(shared ? "threadgroup" : "device")} {Declarations.GetVarTypeName(dstType, true)}*)&{GenerateLoadOrStore(context, operation, isStore: false)}"); - - for (int argIndex = operation.SourcesCount - arity + 2; argIndex < operation.SourcesCount; argIndex++) - { - builder.Append($", {GetSourceExpr(context, operation.GetSource(argIndex), dstType)}, memory_order_relaxed"); - } - } - else - { - for (int argIndex = 0; argIndex < arity; argIndex++) - { - if (argIndex != 0) - { - builder.Append(", "); - } - - AggregateType dstType = GetSrcVarType(inst, argIndex); - - builder.Append(GetSourceExpr(context, operation.GetSource(argIndex), dstType)); - } - - if ((operation.Inst & Instruction.Mask) == Instruction.SwizzleAdd) - { - // SwizzleAdd takes one last argument, the thread_index_in_simdgroup - builder.Append(", thread_index_in_simdgroup"); - } - } - - return $"{info.OpName}({builder})"; - } - else if ((info.Type & InstType.Op) != 0) - { - string op = info.OpName; - - if (inst == Instruction.Return && operation.SourcesCount != 0) - { - return $"{op} {GetSourceExpr(context, operation.GetSource(0), context.CurrentFunction.ReturnType)}"; - } - if (inst == Instruction.Return && context.Definitions.Stage is ShaderStage.Vertex or ShaderStage.Fragment) - { - return $"{op} out"; - } - - int arity = (int)(info.Type & InstType.ArityMask); - - string[] expr = new string[arity]; - - for (int index = 0; index < arity; index++) - { - IAstNode src = operation.GetSource(index); - - string srcExpr = GetSourceExpr(context, src, GetSrcVarType(inst, index)); - - bool isLhs = arity == 2 && index == 0; - - expr[index] = Enclose(srcExpr, src, inst, info, isLhs); - } - - switch (arity) - { - case 0: - return op; - - case 1: - return op + expr[0]; - - case 2: - if (operation.ForcePrecise) - { - string func = (inst & Instruction.Mask) switch - { - Instruction.Add => "PreciseFAdd", - Instruction.Subtract => "PreciseFSub", - Instruction.Multiply => "PreciseFMul", - _ => throw new NotImplementedException() - }; - - return $"{func}({expr[0]}, {expr[1]})"; - } - - return $"{expr[0]} {op} {expr[1]}"; - - case 3: - return $"{expr[0]} {op[0]} {expr[1]} {op[1]} {expr[2]}"; - } - } - else if ((info.Type & InstType.Special) != 0) - { - switch (inst & Instruction.Mask) - { - case Instruction.Ballot: - return Ballot(context, operation); - case Instruction.Call: - return Call(context, operation); - case Instruction.FSIBegin: - case Instruction.FSIEnd: - return "// FSI implemented with raster order groups in MSL"; - case Instruction.GroupMemoryBarrier: - case Instruction.MemoryBarrier: - case Instruction.Barrier: - return Barrier(context, operation); - case Instruction.ImageLoad: - case Instruction.ImageStore: - case Instruction.ImageAtomic: - return ImageLoadOrStore(context, operation); - case Instruction.Load: - return Load(context, operation); - case Instruction.Lod: - return Lod(context, operation); - case Instruction.Store: - return Store(context, operation); - case Instruction.TextureSample: - return TextureSample(context, operation); - case Instruction.TextureQuerySamples: - return TextureQuerySamples(context, operation); - case Instruction.TextureQuerySize: - return TextureQuerySize(context, operation); - case Instruction.PackHalf2x16: - return PackHalf2x16(context, operation); - case Instruction.UnpackHalf2x16: - return UnpackHalf2x16(context, operation); - case Instruction.VectorExtract: - return VectorExtract(context, operation); - case Instruction.VoteAllEqual: - return VoteAllEqual(context, operation); - } - } - - // TODO: Return this to being an error - return $"Unexpected instruction type \"{info.Type}\"."; - } - } -} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenBallot.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenBallot.cs deleted file mode 100644 index 19a065d77..000000000 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenBallot.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Ryujinx.Graphics.Shader.StructuredIr; -using Ryujinx.Graphics.Shader.Translation; - -using static Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions.InstGenHelper; -using static Ryujinx.Graphics.Shader.StructuredIr.InstructionInfo; - -namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions -{ - static class InstGenBallot - { - public static string Ballot(CodeGenContext context, AstOperation operation) - { - AggregateType dstType = GetSrcVarType(operation.Inst, 0); - - string arg = GetSourceExpr(context, operation.GetSource(0), dstType); - char component = "xyzw"[operation.Index]; - - return $"uint4(as_type((simd_vote::vote_t)simd_ballot({arg})), 0, 0).{component}"; - } - - public static string VoteAllEqual(CodeGenContext context, AstOperation operation) - { - AggregateType dstType = GetSrcVarType(operation.Inst, 0); - - string arg = GetSourceExpr(context, operation.GetSource(0), dstType); - - return $"simd_all({arg}) || !simd_any({arg})"; - } - } -} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenBarrier.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenBarrier.cs deleted file mode 100644 index 198b701d6..000000000 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenBarrier.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Ryujinx.Graphics.Shader.IntermediateRepresentation; -using Ryujinx.Graphics.Shader.StructuredIr; - -namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions -{ - static class InstGenBarrier - { - public static string Barrier(CodeGenContext context, AstOperation operation) - { - bool device = (operation.Inst & Instruction.Mask) == Instruction.MemoryBarrier; - - return $"threadgroup_barrier(mem_flags::mem_{(device ? "device" : "threadgroup")})"; - } - } -} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenCall.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenCall.cs deleted file mode 100644 index 98a8a140e..000000000 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenCall.cs +++ /dev/null @@ -1,60 +0,0 @@ -using Ryujinx.Graphics.Shader.StructuredIr; - -using static Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions.InstGenHelper; - -namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions -{ - static class InstGenCall - { - public static string Call(CodeGenContext context, AstOperation operation) - { - AstOperand funcId = (AstOperand)operation.GetSource(0); - - StructuredFunction function = context.GetFunction(funcId.Value); - - int argCount = operation.SourcesCount - 1; - int additionalArgCount = CodeGenContext.AdditionalArgCount + (context.Definitions.Stage != ShaderStage.Compute ? 1 : 0); - bool needsThreadIndex = false; - - // TODO: Replace this with a proper flag - if (function.Name.Contains("Shuffle")) - { - needsThreadIndex = true; - additionalArgCount++; - } - - string[] args = new string[argCount + additionalArgCount]; - - // Additional arguments - if (context.Definitions.Stage != ShaderStage.Compute) - { - args[0] = "in"; - args[1] = "constant_buffers"; - args[2] = "storage_buffers"; - - if (needsThreadIndex) - { - args[3] = "thread_index_in_simdgroup"; - } - } - else - { - args[0] = "constant_buffers"; - args[1] = "storage_buffers"; - - if (needsThreadIndex) - { - args[2] = "thread_index_in_simdgroup"; - } - } - - int argIndex = additionalArgCount; - for (int i = 0; i < argCount; i++) - { - args[argIndex++] = GetSourceExpr(context, operation.GetSource(i + 1), function.GetArgumentType(i)); - } - - return $"{function.Name}({string.Join(", ", args)})"; - } - } -} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenHelper.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenHelper.cs deleted file mode 100644 index 49f3c63aa..000000000 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenHelper.cs +++ /dev/null @@ -1,222 +0,0 @@ -using Ryujinx.Graphics.Shader.IntermediateRepresentation; -using Ryujinx.Graphics.Shader.StructuredIr; -using Ryujinx.Graphics.Shader.Translation; - -using static Ryujinx.Graphics.Shader.CodeGen.Msl.TypeConversion; - -namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions -{ - static class InstGenHelper - { - private static readonly InstInfo[] _infoTable; - - static InstGenHelper() - { - _infoTable = new InstInfo[(int)Instruction.Count]; - -#pragma warning disable IDE0055 // Disable formatting - Add(Instruction.AtomicAdd, InstType.AtomicBinary, "atomic_fetch_add_explicit"); - Add(Instruction.AtomicAnd, InstType.AtomicBinary, "atomic_fetch_and_explicit"); - Add(Instruction.AtomicCompareAndSwap, InstType.AtomicBinary, "atomic_compare_exchange_weak_explicit"); - Add(Instruction.AtomicMaxU32, InstType.AtomicBinary, "atomic_fetch_max_explicit"); - Add(Instruction.AtomicMinU32, InstType.AtomicBinary, "atomic_fetch_min_explicit"); - Add(Instruction.AtomicOr, InstType.AtomicBinary, "atomic_fetch_or_explicit"); - Add(Instruction.AtomicSwap, InstType.AtomicBinary, "atomic_exchange_explicit"); - Add(Instruction.AtomicXor, InstType.AtomicBinary, "atomic_fetch_xor_explicit"); - Add(Instruction.Absolute, InstType.CallUnary, "abs"); - Add(Instruction.Add, InstType.OpBinaryCom, "+", 2); - Add(Instruction.Ballot, InstType.Special); - Add(Instruction.Barrier, InstType.Special); - Add(Instruction.BitCount, InstType.CallUnary, "popcount"); - Add(Instruction.BitfieldExtractS32, InstType.CallTernary, "extract_bits"); - Add(Instruction.BitfieldExtractU32, InstType.CallTernary, "extract_bits"); - Add(Instruction.BitfieldInsert, InstType.CallQuaternary, "insert_bits"); - Add(Instruction.BitfieldReverse, InstType.CallUnary, "reverse_bits"); - Add(Instruction.BitwiseAnd, InstType.OpBinaryCom, "&", 6); - Add(Instruction.BitwiseExclusiveOr, InstType.OpBinaryCom, "^", 7); - Add(Instruction.BitwiseNot, InstType.OpUnary, "~", 0); - Add(Instruction.BitwiseOr, InstType.OpBinaryCom, "|", 8); - Add(Instruction.Call, InstType.Special); - Add(Instruction.Ceiling, InstType.CallUnary, "ceil"); - Add(Instruction.Clamp, InstType.CallTernary, "clamp"); - Add(Instruction.ClampU32, InstType.CallTernary, "clamp"); - Add(Instruction.CompareEqual, InstType.OpBinaryCom, "==", 5); - Add(Instruction.CompareGreater, InstType.OpBinary, ">", 4); - Add(Instruction.CompareGreaterOrEqual, InstType.OpBinary, ">=", 4); - Add(Instruction.CompareGreaterOrEqualU32, InstType.OpBinary, ">=", 4); - Add(Instruction.CompareGreaterU32, InstType.OpBinary, ">", 4); - Add(Instruction.CompareLess, InstType.OpBinary, "<", 4); - Add(Instruction.CompareLessOrEqual, InstType.OpBinary, "<=", 4); - Add(Instruction.CompareLessOrEqualU32, InstType.OpBinary, "<=", 4); - Add(Instruction.CompareLessU32, InstType.OpBinary, "<", 4); - Add(Instruction.CompareNotEqual, InstType.OpBinaryCom, "!=", 5); - Add(Instruction.ConditionalSelect, InstType.OpTernary, "?:", 12); - Add(Instruction.ConvertFP32ToFP64, 0); // MSL does not have a 64-bit FP - Add(Instruction.ConvertFP64ToFP32, 0); // MSL does not have a 64-bit FP - Add(Instruction.ConvertFP32ToS32, InstType.CallUnary, "int"); - Add(Instruction.ConvertFP32ToU32, InstType.CallUnary, "uint"); - Add(Instruction.ConvertFP64ToS32, 0); // MSL does not have a 64-bit FP - Add(Instruction.ConvertFP64ToU32, 0); // MSL does not have a 64-bit FP - Add(Instruction.ConvertS32ToFP32, InstType.CallUnary, "float"); - Add(Instruction.ConvertS32ToFP64, 0); // MSL does not have a 64-bit FP - Add(Instruction.ConvertU32ToFP32, InstType.CallUnary, "float"); - Add(Instruction.ConvertU32ToFP64, 0); // MSL does not have a 64-bit FP - Add(Instruction.Cosine, InstType.CallUnary, "cos"); - Add(Instruction.Ddx, InstType.CallUnary, "dfdx"); - Add(Instruction.Ddy, InstType.CallUnary, "dfdy"); - Add(Instruction.Discard, InstType.CallNullary, "discard_fragment"); - Add(Instruction.Divide, InstType.OpBinary, "/", 1); - Add(Instruction.EmitVertex, 0); // MSL does not have geometry shaders - Add(Instruction.EndPrimitive, 0); // MSL does not have geometry shaders - Add(Instruction.ExponentB2, InstType.CallUnary, "exp2"); - Add(Instruction.FSIBegin, InstType.Special); - Add(Instruction.FSIEnd, InstType.Special); - Add(Instruction.FindLSB, InstType.CallUnary, HelperFunctionNames.FindLSB); - Add(Instruction.FindMSBS32, InstType.CallUnary, HelperFunctionNames.FindMSBS32); - Add(Instruction.FindMSBU32, InstType.CallUnary, HelperFunctionNames.FindMSBU32); - Add(Instruction.Floor, InstType.CallUnary, "floor"); - Add(Instruction.FusedMultiplyAdd, InstType.CallTernary, "fma"); - Add(Instruction.GroupMemoryBarrier, InstType.Special); - Add(Instruction.ImageLoad, InstType.Special); - Add(Instruction.ImageStore, InstType.Special); - Add(Instruction.ImageAtomic, InstType.Special); // Metal 3.1+ - Add(Instruction.IsNan, InstType.CallUnary, "isnan"); - Add(Instruction.Load, InstType.Special); - Add(Instruction.Lod, InstType.Special); - Add(Instruction.LogarithmB2, InstType.CallUnary, "log2"); - Add(Instruction.LogicalAnd, InstType.OpBinaryCom, "&&", 9); - Add(Instruction.LogicalExclusiveOr, InstType.OpBinaryCom, "^", 10); - Add(Instruction.LogicalNot, InstType.OpUnary, "!", 0); - Add(Instruction.LogicalOr, InstType.OpBinaryCom, "||", 11); - Add(Instruction.LoopBreak, InstType.OpNullary, "break"); - Add(Instruction.LoopContinue, InstType.OpNullary, "continue"); - Add(Instruction.PackDouble2x32, 0); // MSL does not have a 64-bit FP - Add(Instruction.PackHalf2x16, InstType.Special); - Add(Instruction.Maximum, InstType.CallBinary, "max"); - Add(Instruction.MaximumU32, InstType.CallBinary, "max"); - Add(Instruction.MemoryBarrier, InstType.Special); - Add(Instruction.Minimum, InstType.CallBinary, "min"); - Add(Instruction.MinimumU32, InstType.CallBinary, "min"); - Add(Instruction.Modulo, InstType.CallBinary, "fmod"); - Add(Instruction.Multiply, InstType.OpBinaryCom, "*", 1); - Add(Instruction.MultiplyHighS32, InstType.CallBinary, "mulhi"); - Add(Instruction.MultiplyHighU32, InstType.CallBinary, "mulhi"); - Add(Instruction.Negate, InstType.OpUnary, "-"); - Add(Instruction.ReciprocalSquareRoot, InstType.CallUnary, "rsqrt"); - Add(Instruction.Return, InstType.OpNullary, "return"); - Add(Instruction.Round, InstType.CallUnary, "round"); - Add(Instruction.ShiftLeft, InstType.OpBinary, "<<", 3); - Add(Instruction.ShiftRightS32, InstType.OpBinary, ">>", 3); - Add(Instruction.ShiftRightU32, InstType.OpBinary, ">>", 3); - Add(Instruction.Shuffle, InstType.CallBinary, "simd_shuffle"); - Add(Instruction.ShuffleDown, InstType.CallBinary, "simd_shuffle_down"); - Add(Instruction.ShuffleUp, InstType.CallBinary, "simd_shuffle_up"); - Add(Instruction.ShuffleXor, InstType.CallBinary, "simd_shuffle_xor"); - Add(Instruction.Sine, InstType.CallUnary, "sin"); - Add(Instruction.SquareRoot, InstType.CallUnary, "sqrt"); - Add(Instruction.Store, InstType.Special); - Add(Instruction.Subtract, InstType.OpBinary, "-", 2); - Add(Instruction.SwizzleAdd, InstType.CallTernary, HelperFunctionNames.SwizzleAdd); - Add(Instruction.TextureSample, InstType.Special); - Add(Instruction.TextureQuerySamples, InstType.Special); - Add(Instruction.TextureQuerySize, InstType.Special); - Add(Instruction.Truncate, InstType.CallUnary, "trunc"); - Add(Instruction.UnpackDouble2x32, 0); // MSL does not have a 64-bit FP - Add(Instruction.UnpackHalf2x16, InstType.Special); - Add(Instruction.VectorExtract, InstType.Special); - Add(Instruction.VoteAll, InstType.CallUnary, "simd_all"); - Add(Instruction.VoteAllEqual, InstType.Special); - Add(Instruction.VoteAny, InstType.CallUnary, "simd_any"); -#pragma warning restore IDE0055 - } - - private static void Add(Instruction inst, InstType flags, string opName = null, int precedence = 0) - { - _infoTable[(int)inst] = new InstInfo(flags, opName, precedence); - } - - public static InstInfo GetInstructionInfo(Instruction inst) - { - return _infoTable[(int)(inst & Instruction.Mask)]; - } - - public static string GetSourceExpr(CodeGenContext context, IAstNode node, AggregateType dstType) - { - return ReinterpretCast(context, node, OperandManager.GetNodeDestType(context, node), dstType); - } - - public static string Enclose(string expr, IAstNode node, Instruction pInst, bool isLhs) - { - InstInfo pInfo = GetInstructionInfo(pInst); - - return Enclose(expr, node, pInst, pInfo, isLhs); - } - - public static string Enclose(string expr, IAstNode node, Instruction pInst, InstInfo pInfo, bool isLhs = false) - { - if (NeedsParenthesis(node, pInst, pInfo, isLhs)) - { - expr = "(" + expr + ")"; - } - - return expr; - } - - public static bool NeedsParenthesis(IAstNode node, Instruction pInst, InstInfo pInfo, bool isLhs) - { - // If the node isn't an operation, then it can only be an operand, - // and those never needs to be surrounded in parentheses. - if (node is not AstOperation operation) - { - // This is sort of a special case, if this is a negative constant, - // and it is consumed by a unary operation, we need to put on the parenthesis, - // as in MSL, while a sequence like ~-1 is valid, --2 is not. - if (IsNegativeConst(node) && pInfo.Type == InstType.OpUnary) - { - return true; - } - - return false; - } - - if ((pInfo.Type & (InstType.Call | InstType.Special)) != 0) - { - return false; - } - - InstInfo info = _infoTable[(int)(operation.Inst & Instruction.Mask)]; - - if ((info.Type & (InstType.Call | InstType.Special)) != 0) - { - return false; - } - - if (info.Precedence < pInfo.Precedence) - { - return false; - } - - if (info.Precedence == pInfo.Precedence && isLhs) - { - return false; - } - - if (pInst == operation.Inst && info.Type == InstType.OpBinaryCom) - { - return false; - } - - return true; - } - - private static bool IsNegativeConst(IAstNode node) - { - if (node is not AstOperand operand) - { - return false; - } - - return operand.Type == OperandType.Constant && operand.Value < 0; - } - } -} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs deleted file mode 100644 index a84a75e4f..000000000 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenMemory.cs +++ /dev/null @@ -1,672 +0,0 @@ -using Ryujinx.Common.Logging; -using Ryujinx.Graphics.Shader.IntermediateRepresentation; -using Ryujinx.Graphics.Shader.StructuredIr; -using Ryujinx.Graphics.Shader.Translation; -using System; -using System.Text; -using static Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions.InstGenHelper; -using static Ryujinx.Graphics.Shader.StructuredIr.InstructionInfo; - -namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions -{ - static class InstGenMemory - { - public static string GenerateLoadOrStore(CodeGenContext context, AstOperation operation, bool isStore) - { - StorageKind storageKind = operation.StorageKind; - - string varName; - AggregateType varType; - int srcIndex = 0; - bool isStoreOrAtomic = operation.Inst == Instruction.Store || operation.Inst.IsAtomic(); - int inputsCount = isStoreOrAtomic ? operation.SourcesCount - 1 : operation.SourcesCount; - bool fieldHasPadding = false; - - if (operation.Inst == Instruction.AtomicCompareAndSwap) - { - inputsCount--; - } - - string fieldName = string.Empty; - switch (storageKind) - { - case StorageKind.ConstantBuffer: - case StorageKind.StorageBuffer: - if (operation.GetSource(srcIndex++) is not AstOperand bindingIndex || bindingIndex.Type != OperandType.Constant) - { - throw new InvalidOperationException($"First input of {operation.Inst} with {storageKind} storage must be a constant operand."); - } - - int binding = bindingIndex.Value; - BufferDefinition buffer = storageKind == StorageKind.ConstantBuffer - ? context.Properties.ConstantBuffers[binding] - : context.Properties.StorageBuffers[binding]; - - if (operation.GetSource(srcIndex++) is not AstOperand fieldIndex || fieldIndex.Type != OperandType.Constant) - { - throw new InvalidOperationException($"Second input of {operation.Inst} with {storageKind} storage must be a constant operand."); - } - - StructureField field = buffer.Type.Fields[fieldIndex.Value]; - - fieldHasPadding = buffer.Layout == BufferLayout.Std140 - && ((field.Type & AggregateType.Vector4) == 0) - && ((field.Type & AggregateType.Array) != 0); - - varName = storageKind == StorageKind.ConstantBuffer - ? "constant_buffers" - : "storage_buffers"; - varName += "." + buffer.Name; - varName += "->" + field.Name; - varType = field.Type; - break; - - case StorageKind.LocalMemory: - case StorageKind.SharedMemory: - if (operation.GetSource(srcIndex++) is not AstOperand { Type: OperandType.Constant } bindingId) - { - throw new InvalidOperationException($"First input of {operation.Inst} with {storageKind} storage must be a constant operand."); - } - - MemoryDefinition memory = storageKind == StorageKind.LocalMemory - ? context.Properties.LocalMemories[bindingId.Value] - : context.Properties.SharedMemories[bindingId.Value]; - - varName = memory.Name; - varType = memory.Type; - break; - - case StorageKind.Input: - case StorageKind.InputPerPatch: - case StorageKind.Output: - case StorageKind.OutputPerPatch: - if (operation.GetSource(srcIndex++) is not AstOperand varId || varId.Type != OperandType.Constant) - { - throw new InvalidOperationException($"First input of {operation.Inst} with {storageKind} storage must be a constant operand."); - } - - IoVariable ioVariable = (IoVariable)varId.Value; - bool isOutput = storageKind.IsOutput(); - bool isPerPatch = storageKind.IsPerPatch(); - int location = -1; - int component = 0; - - if (context.Definitions.HasPerLocationInputOrOutput(ioVariable, isOutput)) - { - if (operation.GetSource(srcIndex++) is not AstOperand vecIndex || vecIndex.Type != OperandType.Constant) - { - throw new InvalidOperationException($"Second input of {operation.Inst} with {storageKind} storage must be a constant operand."); - } - - location = vecIndex.Value; - - if (operation.SourcesCount > srcIndex && - operation.GetSource(srcIndex) is AstOperand elemIndex && - elemIndex.Type == OperandType.Constant && - context.Definitions.HasPerLocationInputOrOutputComponent(ioVariable, vecIndex.Value, elemIndex.Value, isOutput)) - { - component = elemIndex.Value; - srcIndex++; - } - } - - (varName, varType) = IoMap.GetMslBuiltIn( - context.Definitions, - ioVariable, - location, - component, - isOutput, - isPerPatch); - break; - - default: - throw new InvalidOperationException($"Invalid storage kind {storageKind}."); - } - - for (; srcIndex < inputsCount; srcIndex++) - { - IAstNode src = operation.GetSource(srcIndex); - - if ((varType & AggregateType.ElementCountMask) != 0 && - srcIndex == inputsCount - 1 && - src is AstOperand elementIndex && - elementIndex.Type == OperandType.Constant) - { - varName += "." + "xyzw"[elementIndex.Value & 3]; - } - else - { - varName += $"[{GetSourceExpr(context, src, AggregateType.S32)}]"; - } - } - varName += fieldName; - varName += fieldHasPadding ? ".x" : string.Empty; - - if (isStore) - { - varType &= AggregateType.ElementTypeMask; - varName = $"{varName} = {GetSourceExpr(context, operation.GetSource(srcIndex), varType)}"; - } - - return varName; - } - - public static string ImageLoadOrStore(CodeGenContext context, AstOperation operation) - { - AstTextureOperation texOp = (AstTextureOperation)operation; - - bool isArray = (texOp.Type & SamplerType.Array) != 0; - - StringBuilder texCallBuilder = new(); - - int srcIndex = 0; - - string Src(AggregateType type) - { - return GetSourceExpr(context, texOp.GetSource(srcIndex++), type); - } - - string imageName = GetImageName(context, texOp, ref srcIndex); - texCallBuilder.Append(imageName); - texCallBuilder.Append('.'); - - if (texOp.Inst == Instruction.ImageAtomic) - { - texCallBuilder.Append((texOp.Flags & TextureFlags.AtomicMask) switch - { - TextureFlags.Add => "atomic_fetch_add", - TextureFlags.Minimum => "atomic_min", - TextureFlags.Maximum => "atomic_max", - TextureFlags.Increment => "atomic_fetch_add", - TextureFlags.Decrement => "atomic_fetch_sub", - TextureFlags.BitwiseAnd => "atomic_fetch_and", - TextureFlags.BitwiseOr => "atomic_fetch_or", - TextureFlags.BitwiseXor => "atomic_fetch_xor", - TextureFlags.Swap => "atomic_exchange", - TextureFlags.CAS => "atomic_compare_exchange_weak", - _ => "atomic_fetch_add", - }); - } - else - { - texCallBuilder.Append(texOp.Inst == Instruction.ImageLoad ? "read" : "write"); - } - - texCallBuilder.Append('('); - - StringBuilder coordsBuilder = new(); - - int coordsCount = texOp.Type.GetDimensions(); - - if (coordsCount > 1) - { - string[] elems = new string[coordsCount]; - - for (int index = 0; index < coordsCount; index++) - { - elems[index] = Src(AggregateType.S32); - } - - coordsBuilder.Append($"uint{coordsCount}({string.Join(", ", elems)})"); - } - else - { - coordsBuilder.Append($"uint({Src(AggregateType.S32)})"); - } - - if (isArray) - { - coordsBuilder.Append(", "); - coordsBuilder.Append(Src(AggregateType.S32)); - } - - if (texOp.Inst == Instruction.ImageStore) - { - AggregateType type = texOp.Format.GetComponentType(); - - string[] cElems = new string[4]; - - for (int index = 0; index < 4; index++) - { - if (srcIndex < texOp.SourcesCount) - { - cElems[index] = Src(type); - } - else - { - cElems[index] = type switch - { - AggregateType.S32 => NumberFormatter.FormatInt(0), - AggregateType.U32 => NumberFormatter.FormatUint(0), - _ => NumberFormatter.FormatFloat(0), - }; - } - } - - string prefix = type switch - { - AggregateType.S32 => "int", - AggregateType.U32 => "uint", - AggregateType.FP32 => "float", - _ => string.Empty, - }; - - texCallBuilder.Append($"{prefix}4({string.Join(", ", cElems)})"); - texCallBuilder.Append(", "); - } - - texCallBuilder.Append(coordsBuilder); - - if (texOp.Inst == Instruction.ImageAtomic) - { - texCallBuilder.Append(", "); - - AggregateType type = texOp.Format.GetComponentType(); - - if ((texOp.Flags & TextureFlags.AtomicMask) == TextureFlags.CAS) - { - texCallBuilder.Append(Src(type)); // Compare value. - } - - string value = (texOp.Flags & TextureFlags.AtomicMask) switch - { - TextureFlags.Increment => NumberFormatter.FormatInt(1, type), // TODO: Clamp value - TextureFlags.Decrement => NumberFormatter.FormatInt(-1, type), // TODO: Clamp value - _ => Src(type), - }; - - texCallBuilder.Append(value); - // This doesn't match what the MSL spec document says so either - // it is wrong or the MSL compiler has a bug. - texCallBuilder.Append(")[0]"); - } - else - { - texCallBuilder.Append(')'); - - if (texOp.Inst == Instruction.ImageLoad) - { - texCallBuilder.Append(GetMaskMultiDest(texOp.Index)); - } - } - - return texCallBuilder.ToString(); - } - - public static string Load(CodeGenContext context, AstOperation operation) - { - return GenerateLoadOrStore(context, operation, isStore: false); - } - - public static string Lod(CodeGenContext context, AstOperation operation) - { - AstTextureOperation texOp = (AstTextureOperation)operation; - - int coordsCount = texOp.Type.GetDimensions(); - int coordsIndex = 0; - - string textureName = GetTextureName(context, texOp, ref coordsIndex); - string samplerName = GetSamplerName(context, texOp, ref coordsIndex); - - string coordsExpr; - - if (coordsCount > 1) - { - string[] elems = new string[coordsCount]; - - for (int index = 0; index < coordsCount; index++) - { - elems[index] = GetSourceExpr(context, texOp.GetSource(coordsIndex + index), AggregateType.FP32); - } - - coordsExpr = "float" + coordsCount + "(" + string.Join(", ", elems) + ")"; - } - else - { - coordsExpr = GetSourceExpr(context, texOp.GetSource(coordsIndex), AggregateType.FP32); - } - - string clamped = $"{textureName}.calculate_clamped_lod({samplerName}, {coordsExpr})"; - string unclamped = $"{textureName}.calculate_unclamped_lod({samplerName}, {coordsExpr})"; - - return $"float2({clamped}, {unclamped}){GetMask(texOp.Index)}"; - } - - public static string Store(CodeGenContext context, AstOperation operation) - { - return GenerateLoadOrStore(context, operation, isStore: true); - } - - public static string TextureSample(CodeGenContext context, AstOperation operation) - { - AstTextureOperation texOp = (AstTextureOperation)operation; - - bool isGather = (texOp.Flags & TextureFlags.Gather) != 0; - bool hasDerivatives = (texOp.Flags & TextureFlags.Derivatives) != 0; - bool intCoords = (texOp.Flags & TextureFlags.IntCoords) != 0; - bool hasLodBias = (texOp.Flags & TextureFlags.LodBias) != 0; - bool hasLodLevel = (texOp.Flags & TextureFlags.LodLevel) != 0; - bool hasOffset = (texOp.Flags & TextureFlags.Offset) != 0; - bool hasOffsets = (texOp.Flags & TextureFlags.Offsets) != 0; - - bool isArray = (texOp.Type & SamplerType.Array) != 0; - bool isShadow = (texOp.Type & SamplerType.Shadow) != 0; - - StringBuilder texCallBuilder = new(); - - bool colorIsVector = isGather || !isShadow; - - int srcIndex = 0; - - string Src(AggregateType type) - { - return GetSourceExpr(context, texOp.GetSource(srcIndex++), type); - } - - string textureName = GetTextureName(context, texOp, ref srcIndex); - string samplerName = GetSamplerName(context, texOp, ref srcIndex); - - texCallBuilder.Append(textureName); - texCallBuilder.Append('.'); - - if (intCoords) - { - texCallBuilder.Append("read("); - } - else - { - if (isGather) - { - texCallBuilder.Append("gather"); - } - else - { - texCallBuilder.Append("sample"); - } - - if (isShadow) - { - texCallBuilder.Append("_compare"); - } - - texCallBuilder.Append($"({samplerName}, "); - } - - int coordsCount = texOp.Type.GetDimensions(); - - int pCount = coordsCount; - - bool appended = false; - void Append(string str) - { - if (appended) - { - texCallBuilder.Append(", "); - } - else - { - appended = true; - } - - texCallBuilder.Append(str); - } - - AggregateType coordType = intCoords ? AggregateType.S32 : AggregateType.FP32; - - string AssemblePVector(int count) - { - string coords; - if (count > 1) - { - string[] elems = new string[count]; - - for (int index = 0; index < count; index++) - { - elems[index] = Src(coordType); - } - - coords = string.Join(", ", elems); - } - else - { - coords = Src(coordType); - } - - string prefix = intCoords ? "uint" : "float"; - - return prefix + (count > 1 ? count : string.Empty) + "(" + coords + ")"; - } - - Append(AssemblePVector(pCount)); - - if (isArray) - { - Append(Src(AggregateType.S32)); - } - - if (isShadow) - { - Append(Src(AggregateType.FP32)); - } - - if (hasDerivatives) - { - Logger.Warning?.PrintMsg(LogClass.Gpu, "Unused sampler derivatives!"); - } - - if (hasLodBias) - { - Logger.Warning?.PrintMsg(LogClass.Gpu, "Unused sample LOD bias!"); - } - - if (hasLodLevel) - { - if (intCoords) - { - Append(Src(coordType)); - } - else - { - Append($"level({Src(coordType)})"); - } - } - - string AssembleOffsetVector(int count) - { - if (count > 1) - { - string[] elems = new string[count]; - - for (int index = 0; index < count; index++) - { - elems[index] = Src(AggregateType.S32); - } - - return "int" + count + "(" + string.Join(", ", elems) + ")"; - } - else - { - return Src(AggregateType.S32); - } - } - - // TODO: Support reads with offsets - if (!intCoords) - { - if (hasOffset) - { - Append(AssembleOffsetVector(coordsCount)); - } - else if (hasOffsets) - { - Logger.Warning?.PrintMsg(LogClass.Gpu, "Multiple offsets on gathers are not yet supported!"); - } - } - - texCallBuilder.Append(')'); - texCallBuilder.Append(colorIsVector ? GetMaskMultiDest(texOp.Index) : string.Empty); - - return texCallBuilder.ToString(); - } - - private static string GetTextureName(CodeGenContext context, AstTextureOperation texOp, ref int srcIndex) - { - TextureDefinition textureDefinition = context.Properties.Textures[texOp.GetTextureSetAndBinding()]; - string name = textureDefinition.Name; - string setName = Declarations.GetNameForSet(textureDefinition.Set, true); - - if (textureDefinition.ArrayLength != 1) - { - name = $"{name}[{GetSourceExpr(context, texOp.GetSource(srcIndex++), AggregateType.S32)}]"; - } - - return $"{setName}.tex_{name}"; - } - - private static string GetSamplerName(CodeGenContext context, AstTextureOperation texOp, ref int srcIndex) - { - SetBindingPair index = texOp.IsSeparate ? texOp.GetSamplerSetAndBinding() : texOp.GetTextureSetAndBinding(); - int sourceIndex = texOp.IsSeparate ? srcIndex++ : srcIndex + 1; - - TextureDefinition samplerDefinition = context.Properties.Textures[index]; - string name = samplerDefinition.Name; - string setName = Declarations.GetNameForSet(samplerDefinition.Set, true); - - if (samplerDefinition.ArrayLength != 1) - { - name = $"{name}[{GetSourceExpr(context, texOp.GetSource(sourceIndex), AggregateType.S32)}]"; - } - - return $"{setName}.samp_{name}"; - } - - private static string GetImageName(CodeGenContext context, AstTextureOperation texOp, ref int srcIndex) - { - TextureDefinition imageDefinition = context.Properties.Images[texOp.GetTextureSetAndBinding()]; - string name = imageDefinition.Name; - string setName = Declarations.GetNameForSet(imageDefinition.Set, true); - - if (imageDefinition.ArrayLength != 1) - { - name = $"{name}[{GetSourceExpr(context, texOp.GetSource(srcIndex++), AggregateType.S32)}]"; - } - - return $"{setName}.{name}"; - } - - private static string GetMaskMultiDest(int mask) - { - if (mask == 0x0) - { - return string.Empty; - } - - string swizzle = "."; - - for (int i = 0; i < 4; i++) - { - if ((mask & (1 << i)) != 0) - { - swizzle += "xyzw"[i]; - } - } - - return swizzle; - } - - public static string TextureQuerySamples(CodeGenContext context, AstOperation operation) - { - AstTextureOperation texOp = (AstTextureOperation)operation; - - int srcIndex = 0; - - string textureName = GetTextureName(context, texOp, ref srcIndex); - - return $"{textureName}.get_num_samples()"; - } - - public static string TextureQuerySize(CodeGenContext context, AstOperation operation) - { - AstTextureOperation texOp = (AstTextureOperation)operation; - - StringBuilder texCallBuilder = new(); - - int srcIndex = 0; - - string textureName = GetTextureName(context, texOp, ref srcIndex); - texCallBuilder.Append(textureName); - texCallBuilder.Append('.'); - - if (texOp.Index == 3) - { - texCallBuilder.Append("get_num_mip_levels()"); - } - else - { - context.Properties.Textures.TryGetValue(texOp.GetTextureSetAndBinding(), out TextureDefinition definition); - bool hasLod = !definition.Type.HasFlag(SamplerType.Multisample) && (definition.Type & SamplerType.Mask) != SamplerType.TextureBuffer; - bool isArray = definition.Type.HasFlag(SamplerType.Array); - texCallBuilder.Append("get_"); - - if (texOp.Index == 0) - { - texCallBuilder.Append("width"); - } - else if (texOp.Index == 1) - { - texCallBuilder.Append("height"); - } - else - { - if (isArray) - { - texCallBuilder.Append("array_size"); - } - else - { - texCallBuilder.Append("depth"); - } - } - - texCallBuilder.Append('('); - - if (hasLod && !isArray) - { - IAstNode lod = operation.GetSource(0); - string lodExpr = GetSourceExpr(context, lod, GetSrcVarType(operation.Inst, 0)); - - texCallBuilder.Append(lodExpr); - } - - texCallBuilder.Append(')'); - } - - return texCallBuilder.ToString(); - } - - public static string PackHalf2x16(CodeGenContext context, AstOperation operation) - { - IAstNode src0 = operation.GetSource(0); - IAstNode src1 = operation.GetSource(1); - - string src0Expr = GetSourceExpr(context, src0, GetSrcVarType(operation.Inst, 0)); - string src1Expr = GetSourceExpr(context, src1, GetSrcVarType(operation.Inst, 1)); - - return $"as_type(half2({src0Expr}, {src1Expr}))"; - } - - public static string UnpackHalf2x16(CodeGenContext context, AstOperation operation) - { - IAstNode src = operation.GetSource(0); - - string srcExpr = GetSourceExpr(context, src, GetSrcVarType(operation.Inst, 0)); - - return $"float2(as_type({srcExpr})){GetMask(operation.Index)}"; - } - - private static string GetMask(int index) - { - return $".{"xy".AsSpan(index, 1)}"; - } - } -} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenVector.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenVector.cs deleted file mode 100644 index 9d8dae543..000000000 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGenVector.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Ryujinx.Graphics.Shader.IntermediateRepresentation; -using Ryujinx.Graphics.Shader.StructuredIr; - -using static Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions.InstGenHelper; -using static Ryujinx.Graphics.Shader.StructuredIr.InstructionInfo; - -namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions -{ - static class InstGenVector - { - public static string VectorExtract(CodeGenContext context, AstOperation operation) - { - IAstNode vector = operation.GetSource(0); - IAstNode index = operation.GetSource(1); - - string vectorExpr = GetSourceExpr(context, vector, OperandManager.GetNodeDestType(context, vector)); - - if (index is AstOperand indexOperand && indexOperand.Type == OperandType.Constant) - { - char elem = "xyzw"[indexOperand.Value]; - - return $"{vectorExpr}.{elem}"; - } - else - { - string indexExpr = GetSourceExpr(context, index, GetSrcVarType(operation.Inst, 1)); - - return $"{vectorExpr}[{indexExpr}]"; - } - } - } -} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstInfo.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstInfo.cs deleted file mode 100644 index 5e5d04d6b..000000000 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstInfo.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions -{ - readonly struct InstInfo - { - public InstType Type { get; } - - public string OpName { get; } - - public int Precedence { get; } - - public InstInfo(InstType type, string opName, int precedence) - { - Type = type; - OpName = opName; - Precedence = precedence; - } - } -} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstType.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstType.cs deleted file mode 100644 index d8f6bfed1..000000000 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstType.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; -using System.Diagnostics.CodeAnalysis; - -namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions -{ - [Flags] - [SuppressMessage("Design", "CA1069: Enums values should not be duplicated")] - public enum InstType - { - OpNullary = Op | 0, - OpUnary = Op | 1, - OpBinary = Op | 2, - OpBinaryCom = Op | 2 | Commutative, - OpTernary = Op | 3, - - CallNullary = Call | 0, - CallUnary = Call | 1, - CallBinary = Call | 2, - CallTernary = Call | 3, - CallQuaternary = Call | 4, - - // The atomic instructions have one extra operand, - // for the storage slot and offset pair. - AtomicBinary = Call | Atomic | 3, - AtomicTernary = Call | Atomic | 4, - - Commutative = 1 << 8, - Op = 1 << 9, - Call = 1 << 10, - Atomic = 1 << 11, - Special = 1 << 12, - - ArityMask = 0xff, - } -} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs deleted file mode 100644 index 118612c66..000000000 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs +++ /dev/null @@ -1,83 +0,0 @@ -using Ryujinx.Common.Logging; -using Ryujinx.Graphics.Shader.IntermediateRepresentation; -using Ryujinx.Graphics.Shader.Translation; -using System.Globalization; - -namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions -{ - static class IoMap - { - public static (string, AggregateType) GetMslBuiltIn( - ShaderDefinitions definitions, - IoVariable ioVariable, - int location, - int component, - bool isOutput, - bool isPerPatch) - { - (string, AggregateType) returnValue = ioVariable switch - { - IoVariable.BaseInstance => ("base_instance", AggregateType.U32), - IoVariable.BaseVertex => ("base_vertex", AggregateType.U32), - IoVariable.CtaId => ("threadgroup_position_in_grid", AggregateType.Vector3 | AggregateType.U32), - IoVariable.ClipDistance => ("out.clip_distance", AggregateType.Array | AggregateType.FP32), - IoVariable.FragmentOutputColor => ($"out.color{location}", definitions.GetFragmentOutputColorType(location)), - IoVariable.FragmentOutputDepth => ("out.depth", AggregateType.FP32), - IoVariable.FrontFacing => ("in.front_facing", AggregateType.Bool), - IoVariable.GlobalId => ("thread_position_in_grid", AggregateType.Vector3 | AggregateType.U32), - IoVariable.InstanceId => ("instance_id", AggregateType.U32), - IoVariable.InstanceIndex => ("instance_index", AggregateType.U32), - IoVariable.InvocationId => ("INVOCATION_ID", AggregateType.S32), - IoVariable.PointCoord => ("in.point_coord", AggregateType.Vector2 | AggregateType.FP32), - IoVariable.PointSize => ("out.point_size", AggregateType.FP32), - IoVariable.Position => ("out.position", AggregateType.Vector4 | AggregateType.FP32), - IoVariable.PrimitiveId => ("in.primitive_id", AggregateType.U32), - IoVariable.SubgroupEqMask => ("thread_index_in_simdgroup >= 32 ? uint4(0, (1 << (thread_index_in_simdgroup - 32)), uint2(0)) : uint4(1 << thread_index_in_simdgroup, uint3(0))", AggregateType.Vector4 | AggregateType.U32), - IoVariable.SubgroupGeMask => ("uint4(insert_bits(0u, 0xFFFFFFFF, thread_index_in_simdgroup, 32 - thread_index_in_simdgroup), uint3(0)) & (uint4((uint)((simd_vote::vote_t)simd_ballot(true) & 0xFFFFFFFF), (uint)(((simd_vote::vote_t)simd_ballot(true) >> 32) & 0xFFFFFFFF), 0, 0))", AggregateType.Vector4 | AggregateType.U32), - IoVariable.SubgroupGtMask => ("uint4(insert_bits(0u, 0xFFFFFFFF, thread_index_in_simdgroup + 1, 32 - thread_index_in_simdgroup - 1), uint3(0)) & (uint4((uint)((simd_vote::vote_t)simd_ballot(true) & 0xFFFFFFFF), (uint)(((simd_vote::vote_t)simd_ballot(true) >> 32) & 0xFFFFFFFF), 0, 0))", AggregateType.Vector4 | AggregateType.U32), - IoVariable.SubgroupLaneId => ("thread_index_in_simdgroup", AggregateType.U32), - IoVariable.SubgroupLeMask => ("uint4(extract_bits(0xFFFFFFFF, 0, min(thread_index_in_simdgroup + 1, 32u)), extract_bits(0xFFFFFFFF, 0, (uint)max((int)thread_index_in_simdgroup + 1 - 32, 0)), uint2(0))", AggregateType.Vector4 | AggregateType.U32), - IoVariable.SubgroupLtMask => ("uint4(extract_bits(0xFFFFFFFF, 0, min(thread_index_in_simdgroup, 32u)), extract_bits(0xFFFFFFFF, 0, (uint)max((int)thread_index_in_simdgroup - 32, 0)), uint2(0))", AggregateType.Vector4 | AggregateType.U32), - IoVariable.ThreadKill => ("simd_is_helper_thread()", AggregateType.Bool), - IoVariable.UserDefined => GetUserDefinedVariableName(definitions, location, component, isOutput, isPerPatch), - IoVariable.ThreadId => ("thread_position_in_threadgroup", AggregateType.Vector3 | AggregateType.U32), - IoVariable.VertexId => ("vertex_id", AggregateType.S32), - // gl_VertexIndex does not have a direct equivalent in MSL - IoVariable.VertexIndex => ("vertex_id", AggregateType.U32), - IoVariable.ViewportIndex => ("viewport_array_index", AggregateType.S32), - IoVariable.FragmentCoord => ("in.position", AggregateType.Vector4 | AggregateType.FP32), - _ => (null, AggregateType.Invalid), - }; - - if (returnValue.Item2 == AggregateType.Invalid) - { - Logger.Warning?.PrintMsg(LogClass.Gpu, $"Unable to find type for IoVariable {ioVariable}!"); - } - - return returnValue; - } - - private static (string, AggregateType) GetUserDefinedVariableName(ShaderDefinitions definitions, int location, int component, bool isOutput, bool isPerPatch) - { - string name = isPerPatch - ? Defaults.PerPatchAttributePrefix - : (isOutput ? Defaults.OAttributePrefix : Defaults.IAttributePrefix); - - if (location < 0) - { - return (name, definitions.GetUserDefinedType(0, isOutput)); - } - - name += location.ToString(CultureInfo.InvariantCulture); - - if (definitions.HasPerLocationInputOrOutputComponent(IoVariable.UserDefined, location, component, isOutput)) - { - name += "_" + "xyzw"[component & 3]; - } - - string prefix = isOutput ? "out" : "in"; - - return (prefix + "." + name, definitions.GetUserDefinedType(location, isOutput)); - } - } -} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs deleted file mode 100644 index 8ca24fcd3..000000000 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs +++ /dev/null @@ -1,286 +0,0 @@ -using Ryujinx.Common.Logging; -using Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions; -using Ryujinx.Graphics.Shader.StructuredIr; -using Ryujinx.Graphics.Shader.Translation; -using System; -using System.Linq; -using static Ryujinx.Graphics.Shader.CodeGen.Msl.TypeConversion; - -namespace Ryujinx.Graphics.Shader.CodeGen.Msl -{ - static class MslGenerator - { - public static string Generate(StructuredProgramInfo info, CodeGenParameters parameters) - { - if (parameters.Definitions.Stage is not (ShaderStage.Vertex or ShaderStage.Fragment or ShaderStage.Compute)) - { - Logger.Warning?.Print(LogClass.Gpu, $"Attempted to generate unsupported shader type {parameters.Definitions.Stage}!"); - return string.Empty; - } - - CodeGenContext context = new(info, parameters); - - int[] sets = Declarations.Declare(context, info); - - if (info.Functions.Count != 0) - { - for (int i = 1; i < info.Functions.Count; i++) - { - PrintFunction(context, info.Functions[i], parameters.Definitions.Stage, sets); - - context.AppendLine(); - } - } - - PrintFunction(context, info.Functions[0], parameters.Definitions.Stage, sets, true); - - return context.GetCode(); - } - - private static void PrintFunction(CodeGenContext context, StructuredFunction function, ShaderStage stage, int[] sets, bool isMainFunc = false) - { - context.CurrentFunction = function; - - context.AppendLine(GetFunctionSignature(context, function, stage, sets, isMainFunc)); - context.EnterScope(); - - Declarations.DeclareLocals(context, function, stage, isMainFunc); - - PrintBlock(context, function.MainBlock, isMainFunc); - - // In case the shader hasn't returned, return - if (isMainFunc && stage != ShaderStage.Compute) - { - context.AppendLine("return out;"); - } - - context.LeaveScope(); - } - - private static string GetFunctionSignature( - CodeGenContext context, - StructuredFunction function, - ShaderStage stage, - int[] sets, - bool isMainFunc = false) - { - int additionalArgCount = isMainFunc ? 0 : CodeGenContext.AdditionalArgCount + (context.Definitions.Stage != ShaderStage.Compute ? 1 : 0); - bool needsThreadIndex = false; - - // TODO: Replace this with a proper flag - if (function.Name.Contains("Shuffle")) - { - needsThreadIndex = true; - additionalArgCount++; - } - - string[] args = new string[additionalArgCount + function.InArguments.Length + function.OutArguments.Length]; - - // All non-main functions need to be able to access the support_buffer as well - if (!isMainFunc) - { - if (stage != ShaderStage.Compute) - { - args[0] = stage == ShaderStage.Vertex ? "VertexIn in" : "FragmentIn in"; - args[1] = "constant ConstantBuffers &constant_buffers"; - args[2] = "device StorageBuffers &storage_buffers"; - - if (needsThreadIndex) - { - args[3] = "uint thread_index_in_simdgroup"; - } - } - else - { - args[0] = "constant ConstantBuffers &constant_buffers"; - args[1] = "device StorageBuffers &storage_buffers"; - - if (needsThreadIndex) - { - args[2] = "uint thread_index_in_simdgroup"; - } - } - } - - int argIndex = additionalArgCount; - for (int i = 0; i < function.InArguments.Length; i++) - { - args[argIndex++] = $"{Declarations.GetVarTypeName(function.InArguments[i])} {OperandManager.GetArgumentName(i)}"; - } - - for (int i = 0; i < function.OutArguments.Length; i++) - { - int j = i + function.InArguments.Length; - - args[argIndex++] = $"thread {Declarations.GetVarTypeName(function.OutArguments[i])} &{OperandManager.GetArgumentName(j)}"; - } - - string funcKeyword = "inline"; - string funcName = null; - string returnType = Declarations.GetVarTypeName(function.ReturnType); - - if (isMainFunc) - { - if (stage == ShaderStage.Vertex) - { - funcKeyword = "vertex"; - funcName = "vertexMain"; - returnType = "VertexOut"; - } - else if (stage == ShaderStage.Fragment) - { - funcKeyword = "fragment"; - funcName = "fragmentMain"; - returnType = "FragmentOut"; - } - else if (stage == ShaderStage.Compute) - { - funcKeyword = "kernel"; - funcName = "kernelMain"; - returnType = "void"; - } - - if (stage == ShaderStage.Vertex) - { - args = args.Prepend("VertexIn in [[stage_in]]").ToArray(); - } - else if (stage == ShaderStage.Fragment) - { - args = args.Prepend("FragmentIn in [[stage_in]]").ToArray(); - } - - // TODO: add these only if they are used - if (stage == ShaderStage.Vertex) - { - args = args.Append("uint vertex_id [[vertex_id]]").ToArray(); - args = args.Append("uint instance_id [[instance_id]]").ToArray(); - args = args.Append("uint base_instance [[base_instance]]").ToArray(); - args = args.Append("uint base_vertex [[base_vertex]]").ToArray(); - } - else if (stage == ShaderStage.Compute) - { - args = args.Append("uint3 threadgroup_position_in_grid [[threadgroup_position_in_grid]]").ToArray(); - args = args.Append("uint3 thread_position_in_grid [[thread_position_in_grid]]").ToArray(); - args = args.Append("uint3 thread_position_in_threadgroup [[thread_position_in_threadgroup]]").ToArray(); - args = args.Append("uint thread_index_in_simdgroup [[thread_index_in_simdgroup]]").ToArray(); - } - - args = args.Append($"constant ConstantBuffers &constant_buffers [[buffer({Defaults.ConstantBuffersIndex})]]").ToArray(); - args = args.Append($"device StorageBuffers &storage_buffers [[buffer({Defaults.StorageBuffersIndex})]]").ToArray(); - - foreach (int set in sets) - { - long bindingIndex = set + Defaults.BaseSetIndex; - args = args.Append($"constant {Declarations.GetNameForSet(set)} &{Declarations.GetNameForSet(set, true)} [[buffer({bindingIndex})]]").ToArray(); - } - } - - string funcPrefix = $"{funcKeyword} {returnType} {funcName ?? function.Name}("; - string indent = new(' ', funcPrefix.Length); - - return $"{funcPrefix}{string.Join($", \n{indent}", args)})"; - } - - private static void PrintBlock(CodeGenContext context, AstBlock block, bool isMainFunction) - { - AstBlockVisitor visitor = new(block); - - visitor.BlockEntered += (sender, e) => - { - switch (e.Block.Type) - { - case AstBlockType.DoWhile: - context.AppendLine("do"); - break; - - case AstBlockType.Else: - context.AppendLine("else"); - break; - - case AstBlockType.ElseIf: - context.AppendLine($"else if ({GetCondExpr(context, e.Block.Condition)})"); - break; - - case AstBlockType.If: - context.AppendLine($"if ({GetCondExpr(context, e.Block.Condition)})"); - break; - - default: - throw new InvalidOperationException($"Found unexpected block type \"{e.Block.Type}\"."); - } - - context.EnterScope(); - }; - - visitor.BlockLeft += (sender, e) => - { - context.LeaveScope(); - - if (e.Block.Type == AstBlockType.DoWhile) - { - context.AppendLine($"while ({GetCondExpr(context, e.Block.Condition)});"); - } - }; - - bool supportsBarrierDivergence = context.HostCapabilities.SupportsShaderBarrierDivergence; - bool mayHaveReturned = false; - - foreach (IAstNode node in visitor.Visit()) - { - if (node is AstOperation operation) - { - if (!supportsBarrierDivergence) - { - if (operation.Inst == IntermediateRepresentation.Instruction.Barrier) - { - // Barrier on divergent control flow paths may cause the GPU to hang, - // so skip emitting the barrier for those cases. - if (visitor.Block.Type != AstBlockType.Main || mayHaveReturned || !isMainFunction) - { - context.Logger.Log($"Shader has barrier on potentially divergent block, the barrier will be removed."); - - continue; - } - } - else if (operation.Inst == IntermediateRepresentation.Instruction.Return) - { - mayHaveReturned = true; - } - } - - string expr = InstGen.GetExpression(context, operation); - - if (expr != null) - { - context.AppendLine(expr + ";"); - } - } - else if (node is AstAssignment assignment) - { - AggregateType dstType = OperandManager.GetNodeDestType(context, assignment.Destination); - AggregateType srcType = OperandManager.GetNodeDestType(context, assignment.Source); - - string dest = InstGen.GetExpression(context, assignment.Destination); - string src = ReinterpretCast(context, assignment.Source, srcType, dstType); - - context.AppendLine(dest + " = " + src + ";"); - } - else if (node is AstComment comment) - { - context.AppendLine("// " + comment.Comment); - } - else - { - throw new InvalidOperationException($"Found unexpected node type \"{node?.GetType().Name ?? "null"}\"."); - } - } - } - - private static string GetCondExpr(CodeGenContext context, IAstNode cond) - { - AggregateType srcType = OperandManager.GetNodeDestType(context, cond); - - return ReinterpretCast(context, cond, srcType, AggregateType.Bool); - } - } -} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/NumberFormatter.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/NumberFormatter.cs deleted file mode 100644 index 86cdfc0e6..000000000 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/NumberFormatter.cs +++ /dev/null @@ -1,94 +0,0 @@ -using Ryujinx.Graphics.Shader.Translation; -using System; -using System.Globalization; - -namespace Ryujinx.Graphics.Shader.CodeGen.Msl -{ - static class NumberFormatter - { - private const int MaxDecimal = 256; - - public static bool TryFormat(int value, AggregateType dstType, out string formatted) - { - switch (dstType) - { - case AggregateType.FP32: - return TryFormatFloat(BitConverter.Int32BitsToSingle(value), out formatted); - case AggregateType.S32: - formatted = FormatInt(value); - break; - case AggregateType.U32: - formatted = FormatUint((uint)value); - break; - case AggregateType.Bool: - formatted = value != 0 ? "true" : "false"; - break; - default: - throw new ArgumentException($"Invalid variable type \"{dstType}\"."); - } - - return true; - } - - public static string FormatFloat(float value) - { - if (!TryFormatFloat(value, out string formatted)) - { - throw new ArgumentException("Failed to convert float value to string."); - } - - return formatted; - } - - public static bool TryFormatFloat(float value, out string formatted) - { - if (float.IsNaN(value) || float.IsInfinity(value)) - { - formatted = null; - - return false; - } - - formatted = value.ToString("G9", CultureInfo.InvariantCulture); - - if (!(formatted.Contains('.') || - formatted.Contains('e') || - formatted.Contains('E'))) - { - formatted += ".0f"; - } - - return true; - } - - public static string FormatInt(int value, AggregateType dstType) - { - return dstType switch - { - AggregateType.S32 => FormatInt(value), - AggregateType.U32 => FormatUint((uint)value), - _ => throw new ArgumentException($"Invalid variable type \"{dstType}\".") - }; - } - - public static string FormatInt(int value) - { - if (value <= MaxDecimal && value >= -MaxDecimal) - { - return value.ToString(CultureInfo.InvariantCulture); - } - - return $"as_type(0x{value.ToString("X", CultureInfo.InvariantCulture)})"; - } - - public static string FormatUint(uint value) - { - if (value <= MaxDecimal && value >= 0) - { - return value.ToString(CultureInfo.InvariantCulture) + "u"; - } - - return $"as_type(0x{value.ToString("X", CultureInfo.InvariantCulture)})"; - } - } -} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/OperandManager.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/OperandManager.cs deleted file mode 100644 index e131a645e..000000000 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/OperandManager.cs +++ /dev/null @@ -1,176 +0,0 @@ -using Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions; -using Ryujinx.Graphics.Shader.IntermediateRepresentation; -using Ryujinx.Graphics.Shader.StructuredIr; -using Ryujinx.Graphics.Shader.Translation; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using static Ryujinx.Graphics.Shader.StructuredIr.InstructionInfo; - -namespace Ryujinx.Graphics.Shader.CodeGen.Msl -{ - class OperandManager - { - private readonly Dictionary _locals; - - public OperandManager() - { - _locals = new Dictionary(); - } - - public string DeclareLocal(AstOperand operand) - { - string name = $"{Defaults.LocalNamePrefix}_{_locals.Count}"; - - _locals.Add(operand, name); - - return name; - } - - public string GetExpression(CodeGenContext context, AstOperand operand) - { - return operand.Type switch - { - OperandType.Argument => GetArgumentName(operand.Value), - OperandType.Constant => NumberFormatter.FormatInt(operand.Value), - OperandType.LocalVariable => _locals[operand], - OperandType.Undefined => Defaults.UndefinedName, - _ => throw new ArgumentException($"Invalid operand type \"{operand.Type}\"."), - }; - } - - public static string GetArgumentName(int argIndex) - { - return $"{Defaults.ArgumentNamePrefix}{argIndex}"; - } - - public static AggregateType GetNodeDestType(CodeGenContext context, IAstNode node) - { - if (node is AstOperation operation) - { - if (operation.Inst == Instruction.Load || operation.Inst.IsAtomic()) - { - switch (operation.StorageKind) - { - case StorageKind.ConstantBuffer: - case StorageKind.StorageBuffer: - if (operation.GetSource(0) is not AstOperand bindingIndex || bindingIndex.Type != OperandType.Constant) - { - throw new InvalidOperationException($"First input of {operation.Inst} with {operation.StorageKind} storage must be a constant operand."); - } - - if (operation.GetSource(1) is not AstOperand fieldIndex || fieldIndex.Type != OperandType.Constant) - { - throw new InvalidOperationException($"Second input of {operation.Inst} with {operation.StorageKind} storage must be a constant operand."); - } - - BufferDefinition buffer = operation.StorageKind == StorageKind.ConstantBuffer - ? context.Properties.ConstantBuffers[bindingIndex.Value] - : context.Properties.StorageBuffers[bindingIndex.Value]; - StructureField field = buffer.Type.Fields[fieldIndex.Value]; - - return field.Type & AggregateType.ElementTypeMask; - - case StorageKind.LocalMemory: - case StorageKind.SharedMemory: - if (operation.GetSource(0) is not AstOperand { Type: OperandType.Constant } bindingId) - { - throw new InvalidOperationException($"First input of {operation.Inst} with {operation.StorageKind} storage must be a constant operand."); - } - - MemoryDefinition memory = operation.StorageKind == StorageKind.LocalMemory - ? context.Properties.LocalMemories[bindingId.Value] - : context.Properties.SharedMemories[bindingId.Value]; - - return memory.Type & AggregateType.ElementTypeMask; - - case StorageKind.Input: - case StorageKind.InputPerPatch: - case StorageKind.Output: - case StorageKind.OutputPerPatch: - if (operation.GetSource(0) is not AstOperand varId || varId.Type != OperandType.Constant) - { - throw new InvalidOperationException($"First input of {operation.Inst} with {operation.StorageKind} storage must be a constant operand."); - } - - IoVariable ioVariable = (IoVariable)varId.Value; - bool isOutput = operation.StorageKind == StorageKind.Output || operation.StorageKind == StorageKind.OutputPerPatch; - bool isPerPatch = operation.StorageKind == StorageKind.InputPerPatch || operation.StorageKind == StorageKind.OutputPerPatch; - int location = 0; - int component = 0; - - if (context.Definitions.HasPerLocationInputOrOutput(ioVariable, isOutput)) - { - if (operation.GetSource(1) is not AstOperand vecIndex || vecIndex.Type != OperandType.Constant) - { - throw new InvalidOperationException($"Second input of {operation.Inst} with {operation.StorageKind} storage must be a constant operand."); - } - - location = vecIndex.Value; - - if (operation.SourcesCount > 2 && - operation.GetSource(2) is AstOperand elemIndex && - elemIndex.Type == OperandType.Constant && - context.Definitions.HasPerLocationInputOrOutputComponent(ioVariable, location, elemIndex.Value, isOutput)) - { - component = elemIndex.Value; - } - } - - (_, AggregateType varType) = IoMap.GetMslBuiltIn( - context.Definitions, - ioVariable, - location, - component, - isOutput, - isPerPatch); - - return varType & AggregateType.ElementTypeMask; - } - } - else if (operation.Inst == Instruction.Call) - { - AstOperand funcId = (AstOperand)operation.GetSource(0); - - Debug.Assert(funcId.Type == OperandType.Constant); - - return context.GetFunction(funcId.Value).ReturnType; - } - else if (operation.Inst == Instruction.VectorExtract) - { - return GetNodeDestType(context, operation.GetSource(0)) & ~AggregateType.ElementCountMask; - } - else if (operation is AstTextureOperation texOp) - { - if (texOp.Inst == Instruction.ImageLoad || - texOp.Inst == Instruction.ImageStore || - texOp.Inst == Instruction.ImageAtomic) - { - return texOp.GetVectorType(texOp.Format.GetComponentType()); - } - else if (texOp.Inst == Instruction.TextureSample) - { - return texOp.GetVectorType(GetDestVarType(operation.Inst)); - } - } - - return GetDestVarType(operation.Inst); - } - else if (node is AstOperand operand) - { - if (operand.Type == OperandType.Argument) - { - int argIndex = operand.Value; - - return context.CurrentFunction.GetArgumentType(argIndex); - } - - return OperandInfo.GetVarType(operand); - } - else - { - throw new ArgumentException($"Invalid node type \"{node?.GetType().Name ?? "null"}\"."); - } - } - } -} diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/TypeConversion.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/TypeConversion.cs deleted file mode 100644 index e145bb8b0..000000000 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/TypeConversion.cs +++ /dev/null @@ -1,93 +0,0 @@ -using Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions; -using Ryujinx.Graphics.Shader.IntermediateRepresentation; -using Ryujinx.Graphics.Shader.StructuredIr; -using Ryujinx.Graphics.Shader.Translation; -using System; - -namespace Ryujinx.Graphics.Shader.CodeGen.Msl -{ - static class TypeConversion - { - public static string ReinterpretCast( - CodeGenContext context, - IAstNode node, - AggregateType srcType, - AggregateType dstType) - { - if (node is AstOperand operand && operand.Type == OperandType.Constant) - { - if (NumberFormatter.TryFormat(operand.Value, dstType, out string formatted)) - { - return formatted; - } - } - - string expr = InstGen.GetExpression(context, node); - - return ReinterpretCast(expr, node, srcType, dstType); - } - - private static string ReinterpretCast(string expr, IAstNode node, AggregateType srcType, AggregateType dstType) - { - if (srcType == dstType) - { - return expr; - } - - if (srcType == AggregateType.FP32) - { - switch (dstType) - { - case AggregateType.Bool: - return $"(as_type({expr}) != 0)"; - case AggregateType.S32: - return $"as_type({expr})"; - case AggregateType.U32: - return $"as_type({expr})"; - } - } - else if (dstType == AggregateType.FP32) - { - switch (srcType) - { - case AggregateType.Bool: - return $"as_type({ReinterpretBoolToInt(expr, node, AggregateType.S32)})"; - case AggregateType.S32: - return $"as_type({expr})"; - case AggregateType.U32: - return $"as_type({expr})"; - } - } - else if (srcType == AggregateType.Bool) - { - return ReinterpretBoolToInt(expr, node, dstType); - } - else if (dstType == AggregateType.Bool) - { - expr = InstGenHelper.Enclose(expr, node, Instruction.CompareNotEqual, isLhs: true); - - return $"({expr} != 0)"; - } - else if (dstType == AggregateType.S32) - { - return $"int({expr})"; - } - else if (dstType == AggregateType.U32) - { - return $"uint({expr})"; - } - - throw new ArgumentException($"Invalid reinterpret cast from \"{srcType}\" to \"{dstType}\"."); - } - - private static string ReinterpretBoolToInt(string expr, IAstNode node, AggregateType dstType) - { - string trueExpr = NumberFormatter.FormatInt(IrConsts.True, dstType); - string falseExpr = NumberFormatter.FormatInt(IrConsts.False, dstType); - - expr = InstGenHelper.Enclose(expr, node, Instruction.ConditionalSelect, isLhs: false); - - return $"({expr} ? {trueExpr} : {falseExpr})"; - } - } -} diff --git a/src/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj b/src/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj index 8b05d8829..cfb188daf 100644 --- a/src/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj +++ b/src/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj @@ -15,11 +15,4 @@ - - - - - - - diff --git a/src/Ryujinx.Graphics.Shader/SamplerType.cs b/src/Ryujinx.Graphics.Shader/SamplerType.cs index f81d512fa..a693495fa 100644 --- a/src/Ryujinx.Graphics.Shader/SamplerType.cs +++ b/src/Ryujinx.Graphics.Shader/SamplerType.cs @@ -155,51 +155,5 @@ namespace Ryujinx.Graphics.Shader return typeName; } - - public static string ToMslTextureType(this SamplerType type, AggregateType aggregateType, bool image = false) - { - string typeName; - - if ((type & SamplerType.Shadow) != 0) - { - typeName = (type & SamplerType.Mask) switch - { - SamplerType.Texture2D => "depth2d", - SamplerType.TextureCube => "depthcube", - _ => throw new ArgumentException($"Invalid shadow texture type \"{type}\"."), - }; - } - else - { - typeName = (type & SamplerType.Mask) switch - { - SamplerType.Texture1D => "texture1d", - SamplerType.TextureBuffer => "texture_buffer", - SamplerType.Texture2D => "texture2d", - SamplerType.Texture3D => "texture3d", - SamplerType.TextureCube => "texturecube", - _ => throw new ArgumentException($"Invalid texture type \"{type}\"."), - }; - } - - if ((type & SamplerType.Multisample) != 0) - { - typeName += "_ms"; - } - - if ((type & SamplerType.Array) != 0) - { - typeName += "_array"; - } - - string format = aggregateType switch - { - AggregateType.S32 => "int", - AggregateType.U32 => "uint", - _ => "float" - }; - - return $"{typeName}<{format}{(image ? ", access::read_write" : string.Empty)}>"; - } } } diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/HelperFunctionsMask.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/HelperFunctionsMask.cs index b70def78c..2a3d65e75 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/HelperFunctionsMask.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/HelperFunctionsMask.cs @@ -7,14 +7,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr { MultiplyHighS32 = 1 << 2, MultiplyHighU32 = 1 << 3, - - FindLSB = 1 << 5, - FindMSBS32 = 1 << 6, - FindMSBU32 = 1 << 7, - SwizzleAdd = 1 << 10, FSI = 1 << 11, - - Precise = 1 << 13 } } diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs index 1ae669aa9..824aea195 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs @@ -18,10 +18,9 @@ namespace Ryujinx.Graphics.Shader.StructuredIr ShaderDefinitions definitions, ResourceManager resourceManager, TargetLanguage targetLanguage, - bool precise, bool debugMode) { - StructuredProgramContext context = new(attributeUsage, definitions, resourceManager, precise, debugMode); + StructuredProgramContext context = new(attributeUsage, definitions, resourceManager, debugMode); for (int funcIndex = 0; funcIndex < functions.Count; funcIndex++) { @@ -323,9 +322,8 @@ namespace Ryujinx.Graphics.Shader.StructuredIr } // Those instructions needs to be emulated by using helper functions, - // because they are NVIDIA specific or because the target language has - // no direct equivalent. Those flags helps the backend to decide which - // helper functions are needed on the final generated code. + // because they are NVIDIA specific. Those flags helps the backend to + // decide which helper functions are needed on the final generated code. switch (operation.Inst) { case Instruction.MultiplyHighS32: @@ -334,15 +332,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr case Instruction.MultiplyHighU32: context.Info.HelperFunctionsMask |= HelperFunctionsMask.MultiplyHighU32; break; - case Instruction.FindLSB: - context.Info.HelperFunctionsMask |= HelperFunctionsMask.FindLSB; - break; - case Instruction.FindMSBS32: - context.Info.HelperFunctionsMask |= HelperFunctionsMask.FindMSBS32; - break; - case Instruction.FindMSBU32: - context.Info.HelperFunctionsMask |= HelperFunctionsMask.FindMSBU32; - break; case Instruction.SwizzleAdd: context.Info.HelperFunctionsMask |= HelperFunctionsMask.SwizzleAdd; break; diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs index a5887e80d..60bdacc02 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs @@ -36,10 +36,9 @@ namespace Ryujinx.Graphics.Shader.StructuredIr AttributeUsage attributeUsage, ShaderDefinitions definitions, ResourceManager resourceManager, - bool precise, bool debugMode) { - Info = new StructuredProgramInfo(precise); + Info = new StructuredProgramInfo(); Definitions = definitions; ResourceManager = resourceManager; diff --git a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs index 2f8675069..ded2f2a89 100644 --- a/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs +++ b/src/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs @@ -10,16 +10,11 @@ namespace Ryujinx.Graphics.Shader.StructuredIr public HelperFunctionsMask HelperFunctionsMask { get; set; } - public StructuredProgramInfo(bool precise) + public StructuredProgramInfo() { - Functions = []; + Functions = new List(); - IoDefinitions = []; - - if (precise) - { - HelperFunctionsMask |= HelperFunctionsMask.Precise; - } + IoDefinitions = new HashSet(); } } } diff --git a/src/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs b/src/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs index 26c924e89..82a54db83 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs @@ -26,6 +26,5 @@ namespace Ryujinx.Graphics.Shader.Translation SharedMemory = 1 << 11, Store = 1 << 12, VtgAsCompute = 1 << 13, - Precise = 1 << 14, } } diff --git a/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs b/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs index e10182747..94691a5b4 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/ResourceManager.cs @@ -14,7 +14,7 @@ namespace Ryujinx.Graphics.Shader.Translation private const int DefaultLocalMemorySize = 128; private const int DefaultSharedMemorySize = 4096; - private static readonly string[] _stagePrefixes = ["cp", "vp", "tcp", "tep", "gp", "fp"]; + private static readonly string[] _stagePrefixes = new string[] { "cp", "vp", "tcp", "tep", "gp", "fp" }; private readonly IGpuAccessor _gpuAccessor; private readonly ShaderStage _stage; @@ -43,11 +43,6 @@ namespace Ryujinx.Graphics.Shader.Translation private readonly Dictionary _usedTextures; private readonly Dictionary _usedImages; - private readonly List _vacConstantBuffers; - private readonly List _vacStorageBuffers; - private readonly List _vacTextures; - private readonly List _vacImages; - public int LocalMemoryId { get; private set; } public int SharedMemoryId { get; private set; } @@ -78,16 +73,11 @@ namespace Ryujinx.Graphics.Shader.Translation _sbSlots = new(); _sbSlotsReverse = new(); - _usedConstantBufferBindings = []; + _usedConstantBufferBindings = new(); _usedTextures = new(); _usedImages = new(); - _vacConstantBuffers = []; - _vacStorageBuffers = []; - _vacTextures = []; - _vacImages = []; - Properties.AddOrUpdateConstantBuffer(new(BufferLayout.Std140, 0, SupportBuffer.Binding, "support_buffer", SupportBuffer.GetStructureType())); LocalMemoryId = -1; @@ -103,7 +93,7 @@ namespace Ryujinx.Graphics.Shader.Translation size = DefaultLocalMemorySize; } - MemoryDefinition lmem = new("local_memory", AggregateType.Array | AggregateType.U32, BitUtils.DivRoundUp(size, sizeof(uint))); + var lmem = new MemoryDefinition("local_memory", AggregateType.Array | AggregateType.U32, BitUtils.DivRoundUp(size, sizeof(uint))); LocalMemoryId = Properties.AddLocalMemory(lmem); } @@ -122,7 +112,7 @@ namespace Ryujinx.Graphics.Shader.Translation size = DefaultSharedMemorySize; } - MemoryDefinition smem = new("shared_memory", AggregateType.Array | AggregateType.U32, BitUtils.DivRoundUp(size, sizeof(uint))); + var smem = new MemoryDefinition("shared_memory", AggregateType.Array | AggregateType.U32, BitUtils.DivRoundUp(size, sizeof(uint))); SharedMemoryId = Properties.AddSharedMemory(smem); } @@ -283,16 +273,16 @@ namespace Ryujinx.Graphics.Shader.Translation bool coherent, bool separate) { - int dimensions = type == SamplerType.None ? 0 : type.GetDimensions(); - Dictionary dict = isImage ? _usedImages : _usedTextures; + var dimensions = type == SamplerType.None ? 0 : type.GetDimensions(); + var dict = isImage ? _usedImages : _usedTextures; - TextureUsageFlags usageFlags = TextureUsageFlags.None; + var usageFlags = TextureUsageFlags.None; if (intCoords) { usageFlags |= TextureUsageFlags.NeedsScaleValue; - bool canScale = _stage.SupportsRenderScale() && arrayLength == 1 && !write && dimensions == 2; + var canScale = _stage.SupportsRenderScale() && arrayLength == 1 && !write && dimensions == 2; if (!canScale) { @@ -314,9 +304,9 @@ namespace Ryujinx.Graphics.Shader.Translation // For array textures, we also want to use type as key, // since we may have texture handles stores in the same buffer, but for textures with different types. - SamplerType keyType = arrayLength > 1 ? type : SamplerType.None; - TextureInfo info = new(cbufSlot, handle, arrayLength, separate, keyType, format); - TextureMeta meta = new() + var keyType = arrayLength > 1 ? type : SamplerType.None; + var info = new TextureInfo(cbufSlot, handle, arrayLength, separate, keyType, format); + var meta = new TextureMeta() { AccurateType = accurateType, Type = type, @@ -326,7 +316,7 @@ namespace Ryujinx.Graphics.Shader.Translation int setIndex; int binding; - if (dict.TryGetValue(info, out TextureMeta existingMeta)) + if (dict.TryGetValue(info, out var existingMeta)) { dict[info] = MergeTextureMeta(meta, existingMeta); setIndex = existingMeta.Set; @@ -383,7 +373,7 @@ namespace Ryujinx.Graphics.Shader.Translation nameSuffix = cbufSlot < 0 ? $"{prefix}_tcb_{handle:X}" : $"{prefix}_cb{cbufSlot}_{handle:X}"; } - TextureDefinition definition = new( + var definition = new TextureDefinition( setIndex, binding, arrayLength, @@ -443,8 +433,8 @@ namespace Ryujinx.Graphics.Shader.Translation { selectedMeta.UsageFlags |= TextureUsageFlags.NeedsScaleValue; - int dimensions = type.GetDimensions(); - bool canScale = _stage.SupportsRenderScale() && selectedInfo.ArrayLength == 1 && dimensions == 2; + var dimensions = type.GetDimensions(); + var canScale = _stage.SupportsRenderScale() && selectedInfo.ArrayLength == 1 && dimensions == 2; if (!canScale) { @@ -464,7 +454,7 @@ namespace Ryujinx.Graphics.Shader.Translation public BufferDescriptor[] GetConstantBufferDescriptors() { - BufferDescriptor[] descriptors = new BufferDescriptor[_usedConstantBufferBindings.Count]; + var descriptors = new BufferDescriptor[_usedConstantBufferBindings.Count]; int descriptorIndex = 0; @@ -488,7 +478,7 @@ namespace Ryujinx.Graphics.Shader.Translation public BufferDescriptor[] GetStorageBufferDescriptors() { - BufferDescriptor[] descriptors = new BufferDescriptor[_sbSlots.Count]; + var descriptors = new BufferDescriptor[_sbSlots.Count]; int descriptorIndex = 0; @@ -524,7 +514,7 @@ namespace Ryujinx.Graphics.Shader.Translation private static TextureDescriptor[] GetDescriptors(IReadOnlyDictionary usedResources, bool includeArrays) { - List descriptors = []; + List descriptors = new(); bool hasAnyArray = false; @@ -573,75 +563,6 @@ namespace Ryujinx.Graphics.Shader.Translation return descriptors.ToArray(); } - public ShaderProgramInfo GetVertexAsComputeInfo(bool isVertex = false) - { - BufferDescriptor[] cbDescriptors = new BufferDescriptor[_vacConstantBuffers.Count]; - int cbDescriptorIndex = 0; - - foreach (BufferDefinition definition in _vacConstantBuffers) - { - cbDescriptors[cbDescriptorIndex++] = new BufferDescriptor(definition.Set, definition.Binding, 0, 0, 0, BufferUsageFlags.None); - } - - BufferDescriptor[] sbDescriptors = new BufferDescriptor[_vacStorageBuffers.Count]; - int sbDescriptorIndex = 0; - - foreach (BufferDefinition definition in _vacStorageBuffers) - { - sbDescriptors[sbDescriptorIndex++] = new BufferDescriptor(definition.Set, definition.Binding, 0, 0, 0, BufferUsageFlags.Write); - } - - TextureDescriptor[] tDescriptors = new TextureDescriptor[_vacTextures.Count]; - int tDescriptorIndex = 0; - - foreach (TextureDefinition definition in _vacTextures) - { - tDescriptors[tDescriptorIndex++] = new TextureDescriptor( - definition.Set, - definition.Binding, - definition.Type, - definition.Format, - 0, - 0, - definition.ArrayLength, - definition.Separate, - definition.Flags); - } - - TextureDescriptor[] iDescriptors = new TextureDescriptor[_vacImages.Count]; - int iDescriptorIndex = 0; - - foreach (TextureDefinition definition in _vacImages) - { - iDescriptors[iDescriptorIndex++] = new TextureDescriptor( - definition.Set, - definition.Binding, - definition.Type, - definition.Format, - 0, - 0, - definition.ArrayLength, - definition.Separate, - definition.Flags); - } - - return new ShaderProgramInfo( - cbDescriptors, - sbDescriptors, - tDescriptors, - iDescriptors, - isVertex ? ShaderStage.Vertex : ShaderStage.Compute, - 0, - 0, - 0, - false, - false, - false, - false, - 0, - 0); - } - public bool TryGetCbufSlotAndHandleForTexture(int binding, out int cbufSlot, out int handle) { foreach ((TextureInfo info, TextureMeta meta) in _usedTextures) @@ -690,46 +611,24 @@ namespace Ryujinx.Graphics.Shader.Translation private void AddNewConstantBuffer(int setIndex, int binding, string name) { - StructureType type = new([ - new StructureField(AggregateType.Array | AggregateType.Vector4 | AggregateType.FP32, "data", Constants.ConstantBufferSize / 16) - ]); + StructureType type = new(new[] + { + new StructureField(AggregateType.Array | AggregateType.Vector4 | AggregateType.FP32, "data", Constants.ConstantBufferSize / 16), + }); Properties.AddOrUpdateConstantBuffer(new(BufferLayout.Std140, setIndex, binding, name, type)); } private void AddNewStorageBuffer(int setIndex, int binding, string name) { - StructureType type = new([ - new StructureField(AggregateType.Array | AggregateType.U32, "data", 0) - ]); + StructureType type = new(new[] + { + new StructureField(AggregateType.Array | AggregateType.U32, "data", 0), + }); Properties.AddOrUpdateStorageBuffer(new(BufferLayout.Std430, setIndex, binding, name, type)); } - public void AddVertexAsComputeConstantBuffer(BufferDefinition definition) - { - _vacConstantBuffers.Add(definition); - Properties.AddOrUpdateConstantBuffer(definition); - } - - public void AddVertexAsComputeStorageBuffer(BufferDefinition definition) - { - _vacStorageBuffers.Add(definition); - Properties.AddOrUpdateStorageBuffer(definition); - } - - public void AddVertexAsComputeTexture(TextureDefinition definition) - { - _vacTextures.Add(definition); - Properties.AddOrUpdateTexture(definition); - } - - public void AddVertexAsComputeImage(TextureDefinition definition) - { - _vacImages.Add(definition); - Properties.AddOrUpdateImage(definition); - } - public static string GetShaderStagePrefix(ShaderStage stage) { uint index = (uint)stage; diff --git a/src/Ryujinx.Graphics.Shader/Translation/TargetApi.cs b/src/Ryujinx.Graphics.Shader/Translation/TargetApi.cs index 66ed3dd45..519600937 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/TargetApi.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/TargetApi.cs @@ -4,6 +4,5 @@ namespace Ryujinx.Graphics.Shader.Translation { OpenGL, Vulkan, - Metal } } diff --git a/src/Ryujinx.Graphics.Shader/Translation/TargetLanguage.cs b/src/Ryujinx.Graphics.Shader/Translation/TargetLanguage.cs index 9d58cb926..863c7447b 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/TargetLanguage.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/TargetLanguage.cs @@ -4,6 +4,6 @@ namespace Ryujinx.Graphics.Shader.Translation { Glsl, Spirv, - Msl + Arb, } } diff --git a/src/Ryujinx.Graphics.Shader/Translation/Transforms/ForcePreciseEnable.cs b/src/Ryujinx.Graphics.Shader/Translation/Transforms/ForcePreciseEnable.cs index c774816a3..6b7e1410f 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Transforms/ForcePreciseEnable.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Transforms/ForcePreciseEnable.cs @@ -27,8 +27,6 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms addOp.Inst == (Instruction.FP32 | Instruction.Add) && addOp.GetSource(1).Type == OperandType.Constant) { - context.UsedFeatures |= FeatureFlags.Precise; - addOp.ForcePrecise = true; } diff --git a/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs b/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs index ff8fb255a..a579433f9 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs @@ -1,6 +1,5 @@ using Ryujinx.Graphics.Shader.CodeGen; using Ryujinx.Graphics.Shader.CodeGen.Glsl; -using Ryujinx.Graphics.Shader.CodeGen.Msl; using Ryujinx.Graphics.Shader.CodeGen.Spirv; using Ryujinx.Graphics.Shader.Decoders; using Ryujinx.Graphics.Shader.IntermediateRepresentation; @@ -243,8 +242,8 @@ namespace Ryujinx.Graphics.Shader.Translation usedFeatures |= FeatureFlags.VtgAsCompute; } - ControlFlowGraph[] cfgs = new ControlFlowGraph[functions.Length]; - RegisterUsage.FunctionRegisterUsage[] frus = new RegisterUsage.FunctionRegisterUsage[functions.Length]; + var cfgs = new ControlFlowGraph[functions.Length]; + var frus = new RegisterUsage.FunctionRegisterUsage[functions.Length]; for (int i = 0; i < functions.Length; i++) { @@ -267,14 +266,14 @@ namespace Ryujinx.Graphics.Shader.Translation for (int i = 0; i < functions.Length; i++) { - ControlFlowGraph cfg = cfgs[i]; + var cfg = cfgs[i]; int inArgumentsCount = 0; int outArgumentsCount = 0; if (i != 0) { - RegisterUsage.FunctionRegisterUsage fru = frus[i]; + var fru = frus[i]; inArgumentsCount = fru.InArguments.Length; outArgumentsCount = fru.OutArguments.Length; @@ -326,13 +325,12 @@ namespace Ryujinx.Graphics.Shader.Translation FeatureFlags usedFeatures, byte clipDistancesWritten) { - StructuredProgramInfo sInfo = StructuredProgram.MakeStructuredProgram( + var sInfo = StructuredProgram.MakeStructuredProgram( funcs, attributeUsage, definitions, resourceManager, Options.TargetLanguage, - usedFeatures.HasFlag(FeatureFlags.Precise), Options.Flags.HasFlag(TranslationFlags.DebugMode)); int geometryVerticesPerPrimitive = Definitions.OutputTopology switch @@ -342,7 +340,7 @@ namespace Ryujinx.Graphics.Shader.Translation _ => 1 }; - ShaderProgramInfo info = new( + var info = new ShaderProgramInfo( resourceManager.GetConstantBufferDescriptors(), resourceManager.GetStorageBufferDescriptors(), resourceManager.GetTextureDescriptors(), @@ -358,7 +356,7 @@ namespace Ryujinx.Graphics.Shader.Translation clipDistancesWritten, originalDefinitions.OmapTargets); - HostCapabilities hostCapabilities = new( + var hostCapabilities = new HostCapabilities( GpuAccessor.QueryHostReducedPrecision(), GpuAccessor.QueryHostSupportsFragmentShaderInterlock(), GpuAccessor.QueryHostSupportsFragmentShaderOrderingIntel(), @@ -369,13 +367,12 @@ namespace Ryujinx.Graphics.Shader.Translation GpuAccessor.QueryHostSupportsTextureShadowLod(), GpuAccessor.QueryHostSupportsViewportMask()); - CodeGenParameters parameters = new(attributeUsage, definitions, resourceManager.Properties, hostCapabilities, GpuAccessor, Options.TargetApi); + var parameters = new CodeGenParameters(attributeUsage, definitions, resourceManager.Properties, hostCapabilities, GpuAccessor, Options.TargetApi); return Options.TargetLanguage switch { TargetLanguage.Glsl => new ShaderProgram(info, TargetLanguage.Glsl, GlslGenerator.Generate(sInfo, parameters)), TargetLanguage.Spirv => new ShaderProgram(info, TargetLanguage.Spirv, SpirvGenerator.Generate(sInfo, parameters)), - TargetLanguage.Msl => new ShaderProgram(info, TargetLanguage.Msl, MslGenerator.Generate(sInfo, parameters)), _ => throw new NotImplementedException(Options.TargetLanguage.ToString()), }; } @@ -386,15 +383,16 @@ namespace Ryujinx.Graphics.Shader.Translation if (IsTransformFeedbackEmulated) { - StructureType tfeDataStruct = new([ - new(AggregateType.Array | AggregateType.U32, "data", 0) - ]); + StructureType tfeDataStruct = new(new StructureField[] + { + new StructureField(AggregateType.Array | AggregateType.U32, "data", 0) + }); for (int i = 0; i < ResourceReservations.TfeBuffersCount; i++) { int binding = resourceManager.Reservations.GetTfeBufferStorageBufferBinding(i); BufferDefinition tfeDataBuffer = new(BufferLayout.Std430, 1, binding, $"tfe_data{i}", tfeDataStruct); - resourceManager.AddVertexAsComputeStorageBuffer(tfeDataBuffer); + resourceManager.Properties.AddOrUpdateStorageBuffer(tfeDataBuffer); } } @@ -402,21 +400,22 @@ namespace Ryujinx.Graphics.Shader.Translation { int vertexInfoCbBinding = resourceManager.Reservations.VertexInfoConstantBufferBinding; BufferDefinition vertexInfoBuffer = new(BufferLayout.Std140, 0, vertexInfoCbBinding, "vb_info", VertexInfoBuffer.GetStructureType()); - resourceManager.AddVertexAsComputeConstantBuffer(vertexInfoBuffer); + resourceManager.Properties.AddOrUpdateConstantBuffer(vertexInfoBuffer); - StructureType vertexOutputStruct = new([ - new(AggregateType.Array | AggregateType.FP32, "data", 0) - ]); + StructureType vertexOutputStruct = new(new StructureField[] + { + new StructureField(AggregateType.Array | AggregateType.FP32, "data", 0) + }); int vertexOutputSbBinding = resourceManager.Reservations.VertexOutputStorageBufferBinding; BufferDefinition vertexOutputBuffer = new(BufferLayout.Std430, 1, vertexOutputSbBinding, "vertex_output", vertexOutputStruct); - resourceManager.AddVertexAsComputeStorageBuffer(vertexOutputBuffer); + resourceManager.Properties.AddOrUpdateStorageBuffer(vertexOutputBuffer); if (Stage == ShaderStage.Vertex) { SetBindingPair ibSetAndBinding = resourceManager.Reservations.GetIndexBufferTextureSetAndBinding(); TextureDefinition indexBuffer = new(ibSetAndBinding.SetIndex, ibSetAndBinding.Binding, "ib_data", SamplerType.TextureBuffer); - resourceManager.AddVertexAsComputeTexture(indexBuffer); + resourceManager.Properties.AddOrUpdateTexture(indexBuffer); int inputMap = _program.AttributeUsage.UsedInputAttributes; @@ -425,7 +424,7 @@ namespace Ryujinx.Graphics.Shader.Translation int location = BitOperations.TrailingZeroCount(inputMap); SetBindingPair setAndBinding = resourceManager.Reservations.GetVertexBufferTextureSetAndBinding(location); TextureDefinition vaBuffer = new(setAndBinding.SetIndex, setAndBinding.Binding, $"vb_data{location}", SamplerType.TextureBuffer); - resourceManager.AddVertexAsComputeTexture(vaBuffer); + resourceManager.Properties.AddOrUpdateTexture(vaBuffer); inputMap &= ~(1 << location); } @@ -434,19 +433,20 @@ namespace Ryujinx.Graphics.Shader.Translation { SetBindingPair trbSetAndBinding = resourceManager.Reservations.GetTopologyRemapBufferTextureSetAndBinding(); TextureDefinition remapBuffer = new(trbSetAndBinding.SetIndex, trbSetAndBinding.Binding, "trb_data", SamplerType.TextureBuffer); - resourceManager.AddVertexAsComputeTexture(remapBuffer); + resourceManager.Properties.AddOrUpdateTexture(remapBuffer); int geometryVbOutputSbBinding = resourceManager.Reservations.GeometryVertexOutputStorageBufferBinding; BufferDefinition geometryVbOutputBuffer = new(BufferLayout.Std430, 1, geometryVbOutputSbBinding, "geometry_vb_output", vertexOutputStruct); - resourceManager.AddVertexAsComputeStorageBuffer(geometryVbOutputBuffer); + resourceManager.Properties.AddOrUpdateStorageBuffer(geometryVbOutputBuffer); - StructureType geometryIbOutputStruct = new([ - new(AggregateType.Array | AggregateType.U32, "data", 0) - ]); + StructureType geometryIbOutputStruct = new(new StructureField[] + { + new StructureField(AggregateType.Array | AggregateType.U32, "data", 0) + }); int geometryIbOutputSbBinding = resourceManager.Reservations.GeometryIndexOutputStorageBufferBinding; BufferDefinition geometryIbOutputBuffer = new(BufferLayout.Std430, 1, geometryIbOutputSbBinding, "geometry_ib_output", geometryIbOutputStruct); - resourceManager.AddVertexAsComputeStorageBuffer(geometryIbOutputBuffer); + resourceManager.Properties.AddOrUpdateStorageBuffer(geometryIbOutputBuffer); } resourceManager.SetVertexAsComputeLocalMemories(Definitions.Stage, Definitions.InputTopology); @@ -479,40 +479,36 @@ namespace Ryujinx.Graphics.Shader.Translation return new ResourceReservations(GpuAccessor, IsTransformFeedbackEmulated, vertexAsCompute: true, _vertexOutput, ioUsage); } - public ShaderProgramInfo GetVertexAsComputeInfo() - { - return CreateResourceManager(true).GetVertexAsComputeInfo(); - } - public void SetVertexOutputMapForGeometryAsCompute(TranslatorContext vertexContext) { _vertexOutput = vertexContext._program.GetIoUsage(); } - public (ShaderProgram, ShaderProgramInfo) GenerateVertexPassthroughForCompute() + public ShaderProgram GenerateVertexPassthroughForCompute() { - AttributeUsage attributeUsage = new(GpuAccessor); - ResourceManager resourceManager = new(ShaderStage.Vertex, GpuAccessor); + var attributeUsage = new AttributeUsage(GpuAccessor); + var resourceManager = new ResourceManager(ShaderStage.Vertex, GpuAccessor); - ResourceReservations reservations = GetResourceReservations(); + var reservations = GetResourceReservations(); int vertexInfoCbBinding = reservations.VertexInfoConstantBufferBinding; if (Stage == ShaderStage.Vertex) { BufferDefinition vertexInfoBuffer = new(BufferLayout.Std140, 0, vertexInfoCbBinding, "vb_info", VertexInfoBuffer.GetStructureType()); - resourceManager.AddVertexAsComputeConstantBuffer(vertexInfoBuffer); + resourceManager.Properties.AddOrUpdateConstantBuffer(vertexInfoBuffer); } - StructureType vertexInputStruct = new([ - new(AggregateType.Array | AggregateType.FP32, "data", 0) - ]); + StructureType vertexInputStruct = new(new StructureField[] + { + new StructureField(AggregateType.Array | AggregateType.FP32, "data", 0) + }); int vertexDataSbBinding = reservations.VertexOutputStorageBufferBinding; BufferDefinition vertexOutputBuffer = new(BufferLayout.Std430, 1, vertexDataSbBinding, "vb_input", vertexInputStruct); - resourceManager.AddVertexAsComputeStorageBuffer(vertexOutputBuffer); + resourceManager.Properties.AddOrUpdateStorageBuffer(vertexOutputBuffer); - EmitterContext context = new(); + var context = new EmitterContext(); Operand vertexIndex = Options.TargetApi == TargetApi.OpenGL ? context.Load(StorageKind.Input, IoVariable.VertexId) @@ -557,25 +553,25 @@ namespace Ryujinx.Graphics.Shader.Translation } } - Operation[] operations = context.GetOperations(); - ControlFlowGraph cfg = ControlFlowGraph.Create(operations); - Function function = new(cfg.Blocks, "main", false, 0, 0); + var operations = context.GetOperations(); + var cfg = ControlFlowGraph.Create(operations); + var function = new Function(cfg.Blocks, "main", false, 0, 0); - TransformFeedbackOutput[] transformFeedbackOutputs = GetTransformFeedbackOutputs(GpuAccessor, out ulong transformFeedbackVecMap); + var transformFeedbackOutputs = GetTransformFeedbackOutputs(GpuAccessor, out ulong transformFeedbackVecMap); - ShaderDefinitions definitions = new(ShaderStage.Vertex, transformFeedbackVecMap, transformFeedbackOutputs) + var definitions = new ShaderDefinitions(ShaderStage.Vertex, transformFeedbackVecMap, transformFeedbackOutputs) { LastInVertexPipeline = true }; - return (Generate( - [function], + return Generate( + new[] { function }, attributeUsage, definitions, definitions, resourceManager, FeatureFlags.None, - 0), resourceManager.GetVertexAsComputeInfo(isVertex: true)); + 0); } public ShaderProgram GenerateGeometryPassthrough() @@ -608,10 +604,10 @@ namespace Ryujinx.Graphics.Shader.Translation break; } - AttributeUsage attributeUsage = new(GpuAccessor); - ResourceManager resourceManager = new(ShaderStage.Geometry, GpuAccessor); + var attributeUsage = new AttributeUsage(GpuAccessor); + var resourceManager = new ResourceManager(ShaderStage.Geometry, GpuAccessor); - EmitterContext context = new(); + var context = new EmitterContext(); for (int v = 0; v < maxOutputVertices; v++) { @@ -652,11 +648,11 @@ namespace Ryujinx.Graphics.Shader.Translation context.EndPrimitive(); - Operation[] operations = context.GetOperations(); - ControlFlowGraph cfg = ControlFlowGraph.Create(operations); - Function function = new(cfg.Blocks, "main", false, 0, 0); + var operations = context.GetOperations(); + var cfg = ControlFlowGraph.Create(operations); + var function = new Function(cfg.Blocks, "main", false, 0, 0); - ShaderDefinitions definitions = new( + var definitions = new ShaderDefinitions( ShaderStage.Geometry, GpuAccessor.QueryGraphicsState(), false, @@ -665,7 +661,7 @@ namespace Ryujinx.Graphics.Shader.Translation maxOutputVertices); return Generate( - [function], + new[] { function }, attributeUsage, definitions, definitions, diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/IUserLocalCommunicationService.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/IUserLocalCommunicationService.cs index 5c6ee7732..9f7e6206b 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/IUserLocalCommunicationService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/IUserLocalCommunicationService.cs @@ -23,9 +23,6 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator { class IUserLocalCommunicationService : IpcService, IDisposable { - public static string DefaultLanPlayHost = "ryuldn.vudjun.com"; - public static short LanPlayPort = 30456; - public INetworkClient NetworkClient { get; private set; } private const int NifmRequestID = 90; @@ -1092,20 +1089,18 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator case MultiplayerMode.LdnRyu: try { - string ldnServer = context.Device.Configuration.MultiplayerLdnServer; - if (string.IsNullOrEmpty(ldnServer)) - { - ldnServer = DefaultLanPlayHost; - } + string ldnServer = context.Device.Configuration.MultiplayerLdnServer + ?? throw new InvalidOperationException("Cannot initialize RyuLDN with a null Multiplayer server."); + if (!IPAddress.TryParse(ldnServer, out IPAddress ipAddress)) { ipAddress = Dns.GetHostEntry(ldnServer).AddressList[0]; } - NetworkClient = new LdnMasterProxyClient(ipAddress.ToString(), LanPlayPort, context.Device.Configuration); + NetworkClient = new LdnMasterProxyClient(ipAddress.ToString(), SharedConstants.LanPlayPort, context.Device.Configuration); } catch (Exception ex) { - Logger.Error?.Print(LogClass.ServiceLdn, "Could not locate LdnRyu server. Defaulting to stubbed wireless."); + Logger.Error?.Print(LogClass.ServiceLdn, "Could not locate RyuLDN server. Defaulting to stubbed wireless."); Logger.Error?.Print(LogClass.ServiceLdn, ex.Message); NetworkClient = new LdnDisabledClient(); } diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnMitm/LdnMitmClient.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnMitm/LdnMitmClient.cs index 40697d122..35fc783c2 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnMitm/LdnMitmClient.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnMitm/LdnMitmClient.cs @@ -19,7 +19,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnMitm private readonly LanDiscovery _lanDiscovery; - public LdnMitmClient(HLEConfiguration config) + public LdnMitmClient(HleConfiguration config) { UnicastIPAddressInformation localIpInterface = NetworkHelpers.GetLocalInterface(config.MultiplayerLanInterfaceId).Item2; diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/LdnMasterProxyClient.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/LdnMasterProxyClient.cs index 712967180..c2bbcb471 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/LdnMasterProxyClient.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/LdnMasterProxyClient.cs @@ -51,13 +51,13 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu private string _passphrase; private byte[] _gameVersion = new byte[0x10]; - private readonly HLEConfiguration _config; + private readonly HleConfiguration _config; public event EventHandler NetworkChange; public ProxyConfig Config { get; private set; } - public LdnMasterProxyClient(string address, int port, HLEConfiguration config) : base(address, port) + public LdnMasterProxyClient(string address, int port, HleConfiguration config) : base(address, port) { if (ProxyHelpers.SupportsNoDelay()) { diff --git a/src/Ryujinx.HLE/HOS/Services/Nifm/IStaticService.cs b/src/Ryujinx.HLE/HOS/Services/Nifm/IStaticService.cs index 96e71cd07..d669caba1 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nifm/IStaticService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nifm/IStaticService.cs @@ -13,7 +13,7 @@ namespace Ryujinx.HLE.HOS.Services.Nifm // CreateGeneralServiceOld() -> object public ResultCode CreateGeneralServiceOld(ServiceCtx context) { - MakeObject(context, new IGeneralService()); + MakeObject(context, new IGeneralService(context)); return ResultCode.Success; } @@ -22,7 +22,7 @@ namespace Ryujinx.HLE.HOS.Services.Nifm // CreateGeneralService(u64, pid) -> object public ResultCode CreateGeneralService(ServiceCtx context) { - MakeObject(context, new IGeneralService()); + MakeObject(context, new IGeneralService(context)); return ResultCode.Success; } diff --git a/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs b/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs index 656600658..58e609dec 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs @@ -1,4 +1,5 @@ using Ryujinx.Common; +using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; using Ryujinx.Common.Utilities; using Ryujinx.HLE.FileSystem; @@ -17,15 +18,14 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService private IPInterfaceProperties _targetPropertiesCache = null; private UnicastIPAddressInformation _targetAddressInfoCache = null; private string _cacheChosenInterface = null; - private readonly UInt128 _interfaceId = UInt128Utils.CreateRandom(); - - public IGeneralService() + + public IGeneralService(ServiceCtx context) { _generalServiceDetail = new GeneralServiceDetail { ClientId = GeneralServiceManager.Count, - IsAnyInternetRequestAccepted = true, // NOTE: Why not accept any internet request? + IsAnyInternetRequestAccepted = !context.Device.DirtyHacks.IsEnabled(DirtyHack.NifmServiceDisableIsAnyInternetRequestAccepted), // NOTE: Why not accept any internet request? }; NetworkChange.NetworkAddressChanged += LocalInterfaceCacheHandler; diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Proxy/SocketHelpers.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Proxy/SocketHelpers.cs index b442cf802..660803714 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Proxy/SocketHelpers.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Proxy/SocketHelpers.cs @@ -1,6 +1,5 @@ using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy; -using Ryujinx.HLE.HOS.Services.Sockets.Bsd.Types; using System; using System.Collections.Generic; using System.Linq; diff --git a/src/Ryujinx.HLE/HLEConfiguration.cs b/src/Ryujinx.HLE/HleConfiguration.cs similarity index 87% rename from src/Ryujinx.HLE/HLEConfiguration.cs rename to src/Ryujinx.HLE/HleConfiguration.cs index 2ec1d03f5..fc2835b7e 100644 --- a/src/Ryujinx.HLE/HLEConfiguration.cs +++ b/src/Ryujinx.HLE/HleConfiguration.cs @@ -10,7 +10,6 @@ using Ryujinx.HLE.HOS.Services.Ns.Types; using Ryujinx.HLE.HOS.SystemState; using Ryujinx.HLE.UI; using System; -using System.Collections.Generic; using System.Collections.Immutable; namespace Ryujinx.HLE @@ -18,55 +17,55 @@ namespace Ryujinx.HLE /// /// HLE configuration. /// - public class HLEConfiguration + public class HleConfiguration { /// /// The virtual file system used by the FS service. /// /// This cannot be changed after instantiation. - internal readonly VirtualFileSystem VirtualFileSystem; + internal VirtualFileSystem VirtualFileSystem { get; private set; } /// /// The manager for handling a LibHac Horizon instance. /// /// This cannot be changed after instantiation. - internal readonly LibHacHorizonManager LibHacHorizonManager; + internal LibHacHorizonManager LibHacHorizonManager { get; private set; } /// /// The account manager used by the account service. /// /// This cannot be changed after instantiation. - internal readonly AccountManager AccountManager; + internal AccountManager AccountManager { get; private set; } /// /// The content manager used by the NCM service. /// /// This cannot be changed after instantiation. - internal readonly ContentManager ContentManager; + internal ContentManager ContentManager { get; private set; } /// /// The persistent information between run for multi-application capabilities. /// /// This cannot be changed after instantiation. - public readonly UserChannelPersistence UserChannelPersistence; + public UserChannelPersistence UserChannelPersistence { get; private set; } /// /// The GPU renderer to use for all GPU operations. /// /// This cannot be changed after instantiation. - internal readonly IRenderer GpuRenderer; + internal IRenderer GpuRenderer { get; private set; } /// /// The audio device driver to use for all audio operations. /// /// This cannot be changed after instantiation. - internal readonly IHardwareDeviceDriver AudioDeviceDriver; + internal IHardwareDeviceDriver AudioDeviceDriver { get; private set; } /// /// The handler for various UI related operations needed outside of HLE. /// /// This cannot be changed after instantiation. - internal readonly IHostUIHandler HostUIHandler; + internal IHostUIHandler HostUIHandler { get; private set; } /// /// Control the memory configuration used by the emulation context. @@ -197,26 +196,18 @@ namespace Ryujinx.HLE /// /// This cannot be changed after instantiation. public EnabledDirtyHack[] Hacks { internal get; set; } - + /// /// The list of title ids found byApplicationLibrary. /// /// This cannot be changed after instantiation. - internal readonly IImmutableList Titles; + public IImmutableList Titles { internal get; set; } - public HLEConfiguration(VirtualFileSystem virtualFileSystem, - LibHacHorizonManager libHacHorizonManager, - ContentManager contentManager, - AccountManager accountManager, - IImmutableList titles, - UserChannelPersistence userChannelPersistence, - IRenderer gpuRenderer, - IHardwareDeviceDriver audioDeviceDriver, - MemoryConfiguration memoryConfiguration, - IHostUIHandler hostUIHandler, + public HleConfiguration(MemoryConfiguration memoryConfiguration, SystemLanguage systemLanguage, RegionCode region, VSyncMode vSyncMode, + IImmutableList titles, bool enableDockedMode, bool enablePtc, bool enableInternetAccess, @@ -237,19 +228,11 @@ namespace Ryujinx.HLE int customVSyncInterval, EnabledDirtyHack[] dirtyHacks = null) { - VirtualFileSystem = virtualFileSystem; - LibHacHorizonManager = libHacHorizonManager; - AccountManager = accountManager; - ContentManager = contentManager; - Titles = titles; - UserChannelPersistence = userChannelPersistence; - GpuRenderer = gpuRenderer; - AudioDeviceDriver = audioDeviceDriver; MemoryConfiguration = memoryConfiguration; - HostUIHandler = hostUIHandler; SystemLanguage = systemLanguage; Region = region; VSyncMode = vSyncMode; + Titles = titles; CustomVSyncInterval = customVSyncInterval; EnableDockedMode = enableDockedMode; EnablePtc = enablePtc; @@ -270,5 +253,32 @@ namespace Ryujinx.HLE MultiplayerLdnServer = multiplayerLdnServer; Hacks = dirtyHacks ?? []; } + + /// + /// Set the pre-configured services to use for this instance. + /// + public HleConfiguration Configure( + VirtualFileSystem virtualFileSystem, + LibHacHorizonManager libHacHorizonManager, + ContentManager contentManager, + AccountManager accountManager, + UserChannelPersistence userChannelPersistence, + IRenderer gpuRenderer, + IHardwareDeviceDriver audioDeviceDriver, + IHostUIHandler hostUIHandler, + IImmutableList titles + ) + { + VirtualFileSystem = virtualFileSystem; + LibHacHorizonManager = libHacHorizonManager; + AccountManager = accountManager; + ContentManager = contentManager; + UserChannelPersistence = userChannelPersistence; + GpuRenderer = gpuRenderer; + AudioDeviceDriver = audioDeviceDriver; + HostUIHandler = hostUIHandler; + Titles = titles; + return this; + } } } diff --git a/src/Ryujinx.HLE/Loaders/Mods/MemPatch.cs b/src/Ryujinx.HLE/Loaders/Mods/MemPatch.cs index 9a1931433..f601c7749 100644 --- a/src/Ryujinx.HLE/Loaders/Mods/MemPatch.cs +++ b/src/Ryujinx.HLE/Loaders/Mods/MemPatch.cs @@ -71,16 +71,24 @@ namespace Ryujinx.HLE.Loaders.Mods int patchOffset = (int)offset; int patchSize = patch.Length; - if (patchOffset < protectedOffset || patchOffset > memory.Length) + if (patchOffset < protectedOffset) { - continue; // Add warning? + Logger.Warning?.Print(LogClass.ModLoader, $"Attempted to patch protected memory ({patchOffset:x} is within protected boundary of {protectedOffset:x})."); + continue; + } + + if (patchOffset > memory.Length) + { + Logger.Warning?.Print(LogClass.ModLoader, $"Attempted to patch out of bounds memory (offset {patchOffset} ({patchOffset:x}) exceeds memory buffer length {memory.Length})."); + continue; } patchOffset -= protectedOffset; if (patchOffset + patchSize > memory.Length) { - patchSize = memory.Length - patchOffset; // Add warning? + Logger.Warning?.Print(LogClass.ModLoader, $"Patch offset ({patchOffset:x}) + size ({patchSize}) is greater than the size of the memory buffer ({memory.Length}). Attempting to fix this..."); + patchSize = memory.Length - patchOffset; } Logger.Info?.Print(LogClass.ModLoader, $"Patching address offset {patchOffset:x} <= {BitConverter.ToString(patch).Replace('-', ' ')} len={patchSize}"); diff --git a/src/Ryujinx.HLE/Switch.cs b/src/Ryujinx.HLE/Switch.cs index 65bcb275b..0b126a6fa 100644 --- a/src/Ryujinx.HLE/Switch.cs +++ b/src/Ryujinx.HLE/Switch.cs @@ -24,7 +24,7 @@ namespace Ryujinx.HLE { public static Switch Shared { get; private set; } - public HLEConfiguration Configuration { get; } + public HleConfiguration Configuration { get; } public IHardwareDeviceDriver AudioDeviceDriver { get; } public MemoryBlock Memory { get; } public GpuContext Gpu { get; } @@ -48,7 +48,7 @@ namespace Ryujinx.HLE public DirtyHacks DirtyHacks { get; } - public Switch(HLEConfiguration configuration) + public Switch(HleConfiguration configuration) { ArgumentNullException.ThrowIfNull(configuration.GpuRenderer); ArgumentNullException.ThrowIfNull(configuration.AudioDeviceDriver); @@ -98,16 +98,20 @@ namespace Ryujinx.HLE Gpu.GPFifo.DispatchCalls(); } - public void IncrementCustomVSyncInterval() + public int IncrementCustomVSyncInterval() { CustomVSyncInterval += 1; UpdateVSyncInterval(); + + return CustomVSyncInterval; } - public void DecrementCustomVSyncInterval() + public int DecrementCustomVSyncInterval() { CustomVSyncInterval -= 1; UpdateVSyncInterval(); + + return CustomVSyncInterval; } public void UpdateVSyncInterval() diff --git a/src/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj b/src/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj new file mode 100644 index 000000000..fe535e6d5 --- /dev/null +++ b/src/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj @@ -0,0 +1,72 @@ + + + + win-x64;osx-x64;linux-x64 + Exe + true + 1.0.0-dirty + $(DefineConstants);$(ExtraDefineConstants) + - + true + $(DefaultItemExcludes);._* + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Always + THIRDPARTY.md + + + Always + LICENSE.txt + + + + + + Always + + + + + + + + + + false + ..\Ryujinx\Ryujinx.ico + + + + true + true + partial + + diff --git a/src/Ryujinx.Input.SDL2/SDL2Gamepad.cs b/src/Ryujinx.Input.SDL2/SDL2Gamepad.cs index b5fe7fcf8..bf6888940 100644 --- a/src/Ryujinx.Input.SDL2/SDL2Gamepad.cs +++ b/src/Ryujinx.Input.SDL2/SDL2Gamepad.cs @@ -111,7 +111,7 @@ namespace Ryujinx.Input.SDL2 byte blue = packedRgb > 0 ? (byte)(packedRgb % 256) : (byte)0; if (SDL_GameControllerSetLED(_gamepadHandle, red, green, blue) != 0) - Logger.Error?.Print(LogClass.Hid, "LED setting failed; probably in the middle of disconnecting."); + Logger.Debug?.Print(LogClass.Hid, "LED setting failed; probably in the middle of disconnecting."); } private GamepadFeaturesFlag GetFeaturesFlag() diff --git a/src/Ryujinx.ShaderTools/Program.cs b/src/Ryujinx.ShaderTools/Program.cs index 564960c6f..a84d7b466 100644 --- a/src/Ryujinx.ShaderTools/Program.cs +++ b/src/Ryujinx.ShaderTools/Program.cs @@ -116,7 +116,7 @@ namespace Ryujinx.ShaderTools if (options.VertexPassthrough) { - (program, _) = translatorContext.GenerateVertexPassthroughForCompute(); + program = translatorContext.GenerateVertexPassthroughForCompute(); } else { diff --git a/src/Ryujinx/Assets/Styles/Styles.xaml b/src/Ryujinx/Assets/Styles/Styles.xaml index 5523f551a..112815fb5 100644 --- a/src/Ryujinx/Assets/Styles/Styles.xaml +++ b/src/Ryujinx/Assets/Styles/Styles.xaml @@ -1,7 +1,8 @@ - + Content="Add" + Classes="red"/> + + + + + + + + + + @@ -411,7 +440,7 @@ 13 26 28 - 900 + 700 756 diff --git a/src/Ryujinx/Assets/Styles/Themes.xaml b/src/Ryujinx/Assets/Styles/Themes.xaml index 3a0bd4217..de7584240 100644 --- a/src/Ryujinx/Assets/Styles/Themes.xaml +++ b/src/Ryujinx/Assets/Styles/Themes.xaml @@ -1,4 +1,4 @@ - @@ -12,11 +12,13 @@ #C1C1C1 #b3ffffff #80cccccc + #FF6347 #A0000000 #fffcd12a #FF2EEAC9 #FFFF4554 #6483F5 + #800080 #C1C1C1 #b3ffffff #80cccccc + #FF6347 #A0000000 #fffcd12a #13c3a4 #FFFF4554 #6483F5 + #800080 #3D3D3D #0FFFFFFF #1EFFFFFF + #FF6347 #A0FFFFFF #fffcd12a #FF2EEAC9 #FFFF4554 #6483F5 + #FFA500 diff --git a/src/Ryujinx/Assets/locales.json b/src/Ryujinx/Assets/locales.json index 22560ced7..7860387a4 100644 --- a/src/Ryujinx/Assets/locales.json +++ b/src/Ryujinx/Assets/locales.json @@ -242,7 +242,7 @@ "sv_SE": "Direkt musåtkomst", "th_TH": "เข้าถึงเมาส์ได้โดยตรง", "tr_TR": "Doğrudan Mouse Erişimi", - "uk_UA": "Прямий доступ мишею", + "uk_UA": "Пряме керування мишею", "zh_CN": "直通鼠标操作", "zh_TW": "滑鼠直接存取" } @@ -517,7 +517,7 @@ "sv_SE": "Läs in titeluppdateringar från mapp", "th_TH": "โหลดไฟล์อัพเดตจากโฟลเดอร์", "tr_TR": "", - "uk_UA": "Завантажити оновлення заголовків з теки", + "uk_UA": "Завантажити оновлення ігор з теки", "zh_CN": "从文件夹加载游戏更新", "zh_TW": "從資料夾中載入遊戲更新" } @@ -564,11 +564,11 @@ "pl_PL": "", "pt_BR": "Abrir Pasta de Capturas de Tela", "ru_RU": "Открыть папку со скриншотами", - "sv_SE": "", + "sv_SE": "Öppna skärmbildsmappen", "th_TH": "", "tr_TR": "", - "uk_UA": "", - "zh_CN": "", + "uk_UA": "Відкрити теку скріншотів", + "zh_CN": "打开截图文件夹", "zh_TW": "" } }, @@ -717,7 +717,7 @@ "sv_SE": "Starta spel med dolt användargränssnitt", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Запускати ігри з прихованим інтерфейсом", "zh_CN": "启动游戏时隐藏 UI", "zh_TW": "" } @@ -1317,7 +1317,7 @@ "sv_SE": "_Hjälp", "th_TH": "_ช่วยเหลือ", "tr_TR": "_Yardım", - "uk_UA": "_Допомога", + "uk_UA": "_Довідка", "zh_CN": "帮助(_H)", "zh_TW": "說明(_H)" } @@ -1664,10 +1664,10 @@ "pl_PL": "", "pt_BR": "Desenvolvido por {0}", "ru_RU": "Разработана {0}", - "sv_SE": "", + "sv_SE": "Utvecklat av {0}", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Розроблено: {0}", "zh_CN": "由 {0} 开发", "zh_TW": "" } @@ -1764,7 +1764,7 @@ "pl_PL": "Rozszerzenie pliku: {0}", "pt_BR": "Extensão: {0}", "ru_RU": "Расширение файла: {0}", - "sv_SE": "Filänd: {0}", + "sv_SE": "Filändelse: {0}", "th_TH": "นามสกุลไฟล์: {0}", "tr_TR": "Dosya Uzantısı: {0}", "uk_UA": "Розширення файлу: {0}", @@ -1889,7 +1889,7 @@ "pl_PL": "Rozszerzenie pliku", "pt_BR": "Extensão", "ru_RU": "Расширение файла", - "sv_SE": "Filänd", + "sv_SE": "Filändelse", "th_TH": "นามสกุลไฟล์", "tr_TR": "Dosya Uzantısı", "uk_UA": "Розширення файлу", @@ -1964,10 +1964,10 @@ "pl_PL": "", "pt_BR": "Compatibilidade:", "ru_RU": "Совместимость:", - "sv_SE": "", + "sv_SE": "Kompatibilitet:", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Сумісність:", "zh_CN": "兼容性:", "zh_TW": "" } @@ -1989,10 +1989,10 @@ "pl_PL": "", "pt_BR": "ID do Título:", "ru_RU": "ID приложения", - "sv_SE": "", + "sv_SE": "Titel-id:", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "ID гри:", "zh_CN": "标题 ID:", "zh_TW": "" } @@ -2014,10 +2014,10 @@ "pl_PL": "", "pt_BR": "Jogos Hospedados: {0}", "ru_RU": "Запущенно игр: {0}", - "sv_SE": "", + "sv_SE": "Värdskap för spel: {0}", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Розміщені ігри: {0}", "zh_CN": "服务的游戏: {0}", "zh_TW": "" } @@ -2039,10 +2039,10 @@ "pl_PL": "", "pt_BR": "Jogadores Online: {0}", "ru_RU": "Игроков онлайн: {0}", - "sv_SE": "", + "sv_SE": "Online-spelare: {0}", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Гравців онлайн: {0}", "zh_CN": "在线玩家: {0}", "zh_TW": "" } @@ -2389,10 +2389,10 @@ "pl_PL": "", "pt_BR": "Limpar Cache PPTC", "ru_RU": "Очистить кэш PPTC", - "sv_SE": "", + "sv_SE": "Rensa PPTC-cache", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Очистити кеш PPTC", "zh_CN": "清理 PPTC 缓存", "zh_TW": "" } @@ -2414,10 +2414,10 @@ "pl_PL": "", "pt_BR": "Apaga os arquivos de cache PPTC do aplicativo", "ru_RU": "Удаляет все файлы кэша PPTC для приложения", - "sv_SE": "", + "sv_SE": "Tar bort alla PPTC-cachefiler för applikationen", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Видаляє всі файли кешу PPTC для застосунку", "zh_CN": "删除应用程序的所有 PPTC 缓存", "zh_TW": "" } @@ -2742,7 +2742,7 @@ "sv_SE": "Extrahera RomFS från en vald DLC-fil", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Витягти RomFS з обраного файлу DLC", "zh_CN": "从选定的 DLC 文件中解压 RomFS", "zh_TW": "" } @@ -2847,6 +2847,56 @@ "zh_TW": "建立桌面捷徑,啟動選取的應用程式" } }, + { + "ID": "GameListContextMenuCreateCustomConfiguration", + "Translations": { + "ar_SA": "", + "de_DE": "", + "el_GR": "", + "en_US": "Create Custom Configuration", + "es_ES": "", + "fr_FR": "", + "he_IL": "", + "it_IT": "", + "ja_JP": "", + "ko_KR": "", + "no_NO": "", + "pl_PL": "", + "pt_BR": "", + "ru_RU": "Создать пользовательскую конфигурацию", + "sv_SE": "", + "th_TH": "", + "tr_TR": "", + "uk_UA": "Створити користувацьку конфігурацію", + "zh_CN": "创建自定义设置", + "zh_TW": "" + } + }, + { + "ID": "GameListContextMenuEditCustomConfiguration", + "Translations": { + "ar_SA": "", + "de_DE": "", + "el_GR": "", + "en_US": "Edit Custom Configuration", + "es_ES": "", + "fr_FR": "", + "he_IL": "", + "it_IT": "", + "ja_JP": "", + "ko_KR": "", + "no_NO": "", + "pl_PL": "", + "pt_BR": "", + "ru_RU": "Изменить пользовательскую конфигурацию", + "sv_SE": "", + "th_TH": "", + "tr_TR": "", + "uk_UA": "Редагувати користувацьку конфігурацію", + "zh_CN": "编辑自定义设置", + "zh_TW": "" + } + }, { "ID": "GameListContextMenuCreateShortcutToolTipMacOS", "Translations": { @@ -2872,6 +2922,56 @@ "zh_TW": "在 macOS 的應用程式資料夾中建立捷徑,啟動選取的應用程式" } }, + { + "ID": "CreateCustomConfigurationToolTip", + "Translations": { + "ar_SA": "ينشئ تكوينًا مستقلًا للعبة الحالية", + "de_DE": "Erstellt eine unabhängige Konfiguration für das aktuelle Spiel", + "el_GR": "Δημιουργεί μια ανεξάρτητη διαμόρφωση για το τρέχον παιχνίδι", + "en_US": "Creates an independent configuration for the selected game", + "es_ES": "Crea una configuración independiente para el juego actual", + "fr_FR": "Crée une configuration indépendante pour le jeu en cours", + "he_IL": "יוצר תצורה עצמאית למשחק הנוכחי", + "it_IT": "Crea una configurazione indipendente per il gioco attuale", + "ja_JP": "現在のゲーム用の独立した設定を作成します", + "ko_KR": "현재 게임에 대한 독립적인 설정을 생성합니다", + "no_NO": "Oppretter en uavhengig konfigurasjon for det gjeldende spillet", + "pl_PL": "Tworzy niezależną konfigurację dla bieżącej gry", + "pt_BR": "Cria uma configuração independente para o jogo atual", + "ru_RU": "Создает независимую конфигурацию для текущей игры", + "sv_SE": "Skapar en oberoende konfiguration för det aktuella spelet", + "th_TH": "สร้างการกำหนดค่าที่เป็นอิสระสำหรับเกมปัจจุบัน", + "tr_TR": "Mevcut oyun için bağımsız bir yapılandırma oluşturur", + "uk_UA": "Створюйте незалежну конфігурацію для поточної гри", + "zh_CN": "为当前游戏创建独立的配置", + "zh_TW": "為當前遊戲創建獨立的配置" + } + }, + { + "ID": "EditCustomConfigurationToolTip", + "Translations": { + "ar_SA": "", + "de_DE": "", + "el_GR": "", + "en_US": "Edit your existing independent configuration for the selected game", + "es_ES": "", + "fr_FR": "", + "he_IL": "", + "it_IT": "", + "ja_JP": "", + "ko_KR": "", + "no_NO": "", + "pl_PL": "", + "pt_BR": "", + "ru_RU": "Отредактировать существующую независимую конфигурацию для выбранной игры.", + "sv_SE": "", + "th_TH": "", + "tr_TR": "", + "uk_UA": "Відредагувати наявну індивідуальну конфігурацію для цієї гри.", + "zh_CN": "编辑选定游戏的现存独立配置", + "zh_TW": "" + } + }, { "ID": "GameListContextMenuShowCompatEntry", "Translations": { @@ -2889,10 +2989,10 @@ "pl_PL": "", "pt_BR": "Mostrar Dados de Compatibilidade", "ru_RU": "Показать записи о совместимости", - "sv_SE": "", + "sv_SE": "Visa kompatibilitetspost", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Iнформація про сумісність", "zh_CN": "显示兼容性项目", "zh_TW": "" } @@ -2914,10 +3014,10 @@ "pl_PL": "", "pt_BR": "Exibe o jogo selecionado na Lista de Compatibilidade, que normalmente pode ser acessada pelo menu Ajuda.", "ru_RU": "Отобразить выбранную игру в списке совместимости, доступ к которому вы обычно можете получить через меню Справки.", - "sv_SE": "", + "sv_SE": "Visa valt spel i kompatibilitetslistan som du normalt sett kan komma åt via hjälpmenyn.", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Показати цю гру в Списку Сумісності. Список сумісності також можна зайти в меню Довідки.", "zh_CN": "在兼容性列表中显示选定的游戏,您通常可以通过帮助菜单访问。", "zh_TW": "" } @@ -2939,10 +3039,10 @@ "pl_PL": "", "pt_BR": "Mostrar Informações do Jogo", "ru_RU": "Показать информацию об игре", - "sv_SE": "", + "sv_SE": "Visa spelinformation", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Інформація про гру", "zh_CN": "显示游戏信息", "zh_TW": "" } @@ -2964,10 +3064,10 @@ "pl_PL": "", "pt_BR": "Exibe estatísticas e detalhes sobre o jogo selecionado.", "ru_RU": "Показывать статистику и подробную информацию о выбранной игре.", - "sv_SE": "", + "sv_SE": "Visa statistik och detaljer om det aktuella spelet.", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Показати статистику та деталі обраної гри.", "zh_CN": "显示当前选定游戏的状态与详细信息。", "zh_TW": "" } @@ -3397,6 +3497,31 @@ "zh_TW": "設定" } }, + { + "ID": "SettingsWithInfo", + "Translations": { + "ar_SA": "{0} - إعدادات", + "de_DE": "Einstellungen - {0}", + "el_GR": "Ρυθμίσεις - {0}", + "en_US": "Settings - {0}", + "es_ES": "Configuración - {0}", + "fr_FR": "Paramètres - {0}", + "he_IL": "{0} - הגדרות", + "it_IT": "Impostazioni - {0}", + "ja_JP": "設定 - {0}", + "ko_KR": "설정 - {0}", + "no_NO": "Innstillinger - {0}", + "pl_PL": "Ustawienia - {0}", + "pt_BR": "Configurações - {0}", + "ru_RU": "Параметры - {0}", + "sv_SE": "Inställningar - {0}", + "th_TH": "ตั้งค่า - {0}", + "tr_TR": "Ayarlar - {0}", + "uk_UA": "Налаштування - {0}", + "zh_CN": "设置 - {0}", + "zh_TW": "設定 - {0}" + } + }, { "ID": "SettingsTabGeneral", "Translations": { @@ -3417,7 +3542,7 @@ "sv_SE": "Användargränssnitt", "th_TH": "หน้าจอผู้ใช้", "tr_TR": "Kullancı Arayüzü", - "uk_UA": "Інтерфейс користувача", + "uk_UA": "Інтерфейс", "zh_CN": "用户界面", "zh_TW": "使用者介面" } @@ -3489,10 +3614,10 @@ "pl_PL": "", "pt_BR": "Verificar Atualizações:", "ru_RU": "Проверка наличия обновлений", - "sv_SE": "", + "sv_SE": "Leta efter uppdateringar:", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Перевірка оновлень:", "zh_CN": "检查更新", "zh_TW": "" } @@ -3514,10 +3639,10 @@ "pl_PL": "", "pt_BR": "Desligado", "ru_RU": "Отключить", - "sv_SE": "", + "sv_SE": "Av", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Вимкнути", "zh_CN": "关闭", "zh_TW": "" } @@ -3539,10 +3664,10 @@ "pl_PL": "", "pt_BR": "Ao Abrir", "ru_RU": "При запуске", - "sv_SE": "", + "sv_SE": "Fråga", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Запитувати щоразу", "zh_CN": "提示", "zh_TW": "" } @@ -3564,10 +3689,10 @@ "pl_PL": "", "pt_BR": "2° Plano", "ru_RU": "В фоне", - "sv_SE": "", + "sv_SE": "Bakgrund", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Оновлювати в фоні", "zh_CN": "背景", "zh_TW": "" } @@ -3589,10 +3714,10 @@ "pl_PL": "", "pt_BR": "Ao Perder o Foco:", "ru_RU": "При выходе эмулятора из фокуса", - "sv_SE": "", + "sv_SE": "När emulatorn tappar fokus:", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "При втраті фокуса емулятором:", "zh_CN": "当模拟器在后台时:", "zh_TW": "" } @@ -3614,10 +3739,10 @@ "pl_PL": "", "pt_BR": "Não Fazer Nada", "ru_RU": "Ничего не делать", - "sv_SE": "", + "sv_SE": "Gör ingenting", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Нічого не робити", "zh_CN": "什么事情也不做", "zh_TW": "" } @@ -3639,10 +3764,10 @@ "pl_PL": "", "pt_BR": "Bloquear Controles", "ru_RU": "Блокировать управление", - "sv_SE": "", + "sv_SE": "Blockera inmatning", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Блокувати введення", "zh_CN": "禁用输入", "zh_TW": "" } @@ -3664,10 +3789,10 @@ "pl_PL": "", "pt_BR": "Ficar Mudo", "ru_RU": "Отключить звук", - "sv_SE": "", + "sv_SE": "Stäng av ljudet", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Вимкнути звук", "zh_CN": "静音", "zh_TW": "" } @@ -3689,10 +3814,10 @@ "pl_PL": "", "pt_BR": "Bloquear Controles & Ficar Mudo", "ru_RU": "Блокировать управление и отключить звук", - "sv_SE": "", + "sv_SE": "Blockera inmatningar och stäng av ljudet", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Блокувати введення та Вимкнути звук", "zh_CN": "阻止输入且静音", "zh_TW": "" } @@ -3714,10 +3839,10 @@ "pl_PL": "", "pt_BR": "Pausar a Emulação", "ru_RU": "Поставить паузу", - "sv_SE": "", + "sv_SE": "Pausa emuleringen", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Поставити на паузу", "zh_CN": "暂停模拟", "zh_TW": "" } @@ -3789,37 +3914,62 @@ "pl_PL": "", "pt_BR": "Desativar Controles Quando Estiver Fora de Foco", "ru_RU": "Отключает управление при выходе из фокуса", - "sv_SE": "", + "sv_SE": "Inaktivera inmatning när fokus tappas", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Вимкнути введення, якщо вікно неактивне", "zh_CN": "在后台时禁用输入", "zh_TW": "" } }, { - "ID": "SettingsTabGeneralShowTitleBar", + "ID": "SettingsTabGeneralShowOldUI", "Translations": { "ar_SA": "", "de_DE": "", "el_GR": "", - "en_US": "Show Title Bar (Requires restart)", - "es_ES": "Mostrar Barra de Título (Requiere reinicio)", - "fr_FR": "Afficher Barre de Titre (Nécessite redémarrage)", + "en_US": "Show Original UI Style (Requires restart)", + "es_ES": "", + "fr_FR": "", "he_IL": "", - "it_IT": "Mostra barra del titolo (Richiede il riavvio)", + "it_IT": "", "ja_JP": "", - "ko_KR": "제목 표시줄 표시(다시 시작해야 함)", - "no_NO": "Vis tittellinje (krever omstart)", + "ko_KR": "", + "no_NO": "Vis original UI-stil (krever omstart)", "pl_PL": "", - "pt_BR": "Mostrar Barra de Título (Requer reinicialização)", - "ru_RU": "Показать строку заголовка (требуется перезапуск)", - "sv_SE": "Visa titelrad (kräver omstart)", + "pt_BR": "", + "ru_RU": "", + "sv_SE": "", "th_TH": "", "tr_TR": "", - "uk_UA": "Показувати рядок заголовка (Потрібен перезапуск)", - "zh_CN": "显示标题栏 (需要重启)", - "zh_TW": "顯示「標題列」 (需要重新開啟Ryujinx)" + "uk_UA": "Показати оригінальний UI (Потрібен перезапуск)", + "zh_CN": "显示原始 UI 样式 (需要重启)", + "zh_TW": "" + } + }, + { + "ID": "SettingsTabGeneralShowOldUIToolTip", + "Translations": { + "ar_SA": "", + "de_DE": "", + "el_GR": "", + "en_US": "Show the older Avalonia Ryujinx UI reminiscent of Ryujinx 1.1.1403. This is enabled by default on platforms that are not Windows.\nThe classic-style title bar is back and major window layout reworkings are reversed; such as the settings navigation placement above this tooltip.", + "es_ES": "", + "fr_FR": "", + "he_IL": "", + "it_IT": "", + "ja_JP": "", + "ko_KR": "", + "no_NO": "Vis det eldre Avalonia Ryujinx-grensesnittet som minner om Ryujinx 1.1.1403. Dette er aktivert som standard på plattformer som ikke er Windows.\nTittellinjen i klassisk stil er tilbake, og store omarbeidinger av vindusoppsettet er reversert, for eksempel plasseringen av innstillingsnavigasjonen over dette verktøytipset.", + "pl_PL": "", + "pt_BR": "", + "ru_RU": "", + "sv_SE": "", + "th_TH": "", + "tr_TR": "", + "uk_UA": "Показати старий інтерфейс Avalonia Ryujinx, який був у Ryujinx 1.1.1403. Ця опція активна за замовчуванням на всіх інших, окрім Windows платформах.\nПовернеться класична панель заголовка, а всі суттєві зміни інтерфейсу будуть скасовані, зокрема горизонтальне розміщення навігації в налаштуваннях.", + "zh_CN": "显示旧的类似 Ryujinx 1.1.1403 的 Avalonia Ryujinx UI。在非 Windows 平台上默认启用此选项。\n经典样式的标题栏已回归并且恢复了对窗口布局的重大重构;例如在工具提示上方放置设置导航。", + "zh_TW": "" } }, { @@ -3842,7 +3992,7 @@ "sv_SE": "Dölj markör:", "th_TH": "ซ่อน เคอร์เซอร์:", "tr_TR": "İşaretçiyi Gizle:", - "uk_UA": "Сховати вказівник:", + "uk_UA": "Сховати курсор:", "zh_CN": "隐藏鼠标指针:", "zh_TW": "隱藏滑鼠游標:" } @@ -3964,7 +4114,7 @@ "pl_PL": "", "pt_BR": "Carregar Automaticamente Pasta de DLC e Atualizações", "ru_RU": "Автозагрузка папки с DLC/Обновлениями", - "sv_SE": "Läs automatisk in DLC/speluppdateringar", + "sv_SE": "Läs automatiskt in DLC/speluppdateringar", "th_TH": "โหลดไดเรกทอรี DLC/ไฟล์อัปเดต อัตโนมัติ", "tr_TR": "", "uk_UA": "Автозавантаження теки DLC/Оновлень", @@ -4814,10 +4964,10 @@ "pl_PL": "", "pt_BR": "Sincronizar com o Sistema PC", "ru_RU": "Соответствовать времени в системе", - "sv_SE": "", + "sv_SE": "Matcha systemtid", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Синхронізувати з системним годинником", "zh_CN": "与系统时间同步", "zh_TW": "" } @@ -4988,7 +5138,7 @@ "no_NO": "Lyd Inn/Ut", "pl_PL": "", "pt_BR": "", - "ru_RU": "", + "ru_RU": "Выход/Вход звука", "sv_SE": "", "th_TH": "", "tr_TR": "", @@ -5239,11 +5389,11 @@ "pl_PL": "", "pt_BR": "Ignorar Applet do Controlador", "ru_RU": "Игнорировать апплет контроллера", - "sv_SE": "", + "sv_SE": "Ignorera kontroller-applet", "th_TH": "", "tr_TR": "", - "uk_UA": "", - "zh_CN": "", + "uk_UA": "Ігнорувати Аплет Контролера", + "zh_CN": "忽略控制器小程序", "zh_TW": "" } }, @@ -6114,10 +6264,10 @@ "pl_PL": "", "pt_BR": "Habilitar Logs da IU", "ru_RU": "Включить журнал интерфейса", - "sv_SE": "", + "sv_SE": "Aktivera gränssnittsloggar", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Увімкнути журнали інтерфейсу", "zh_CN": "启用 UI 日志", "zh_TW": "" } @@ -6142,7 +6292,7 @@ "sv_SE": "Aktivera loggar för filsystemsåtkomst", "th_TH": "เปิดใช้งานการบันทึกประวัติการเข้าถึง Fs", "tr_TR": "Fs Erişim Loglarını Etkinleştir", - "uk_UA": "Увімкнути журнали доступу Fs", + "uk_UA": "Увімкнути журнали доступу до файлової системи", "zh_CN": "启用文件访问日志", "zh_TW": "啟用檔案系統存取日誌" } @@ -6167,7 +6317,7 @@ "sv_SE": "Loggläge för global filsystemsåtkomst:", "th_TH": "โหมด การเข้าถึงประวัติส่วนกลาง:", "tr_TR": "Fs Evrensel Erişim Log Modu:", - "uk_UA": "Режим журналу глобального доступу Fs:", + "uk_UA": "Режим журналу глобального доступу файлової системи:", "zh_CN": "文件系统全局访问日志模式:", "zh_TW": "檔案系統全域存取日誌模式:" } @@ -6392,7 +6542,7 @@ "sv_SE": "Inmatning", "th_TH": "ป้อนข้อมูล", "tr_TR": "Giriş Yöntemi", - "uk_UA": "Введення", + "uk_UA": "Керування", "zh_CN": "输入", "zh_TW": "輸入" } @@ -6514,10 +6664,10 @@ "pl_PL": "", "pt_BR": "Redefinir Configurações", "ru_RU": "Сбросить настройки", - "sv_SE": "", + "sv_SE": "Nollställ inställningar", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Скинути налаштування", "zh_CN": "重置设置", "zh_TW": "" } @@ -6539,10 +6689,10 @@ "pl_PL": "", "pt_BR": "Quero redefinir minhas configurações.", "ru_RU": "Я хочу сбросить свои настройки.", - "sv_SE": "", + "sv_SE": "Jag vill nollställa mina inställningar.", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Я хочу скинути налаштування.", "zh_CN": "我要重置我的设置。", "zh_TW": "" } @@ -6563,7 +6713,7 @@ "no_NO": "", "pl_PL": "", "pt_BR": "", - "ru_RU": "", + "ru_RU": "Ок", "sv_SE": "Ok", "th_TH": "ตกลง", "tr_TR": "Tamam", @@ -7013,7 +7163,7 @@ "no_NO": "", "pl_PL": "Pro Kontroler", "pt_BR": "", - "ru_RU": "", + "ru_RU": "Pro контроллер", "sv_SE": "", "th_TH": "โปรคอนโทรลเลอร์", "tr_TR": "Profesyonel Kumanda", @@ -7239,7 +7389,7 @@ "pl_PL": "Przyciski", "pt_BR": "Botões", "ru_RU": "Кнопки", - "sv_SE": "Knappar", + "sv_SE": "Handlingsknappar", "th_TH": "ปุ่มกด", "tr_TR": "Tuşlar", "uk_UA": "Кнопки", @@ -8492,7 +8642,7 @@ "sv_SE": "", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "LED-підсвітка", "zh_CN": "", "zh_TW": "" } @@ -8517,7 +8667,7 @@ "sv_SE": "Inaktivera", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Вимкнути", "zh_CN": "关闭", "zh_TW": "" } @@ -8542,7 +8692,7 @@ "sv_SE": "Regnbåge", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Веселка", "zh_CN": "彩虹", "zh_TW": "" } @@ -8564,10 +8714,10 @@ "pl_PL": "", "pt_BR": "Velocidade do Arco-íris", "ru_RU": "Скорость переливания", - "sv_SE": "", + "sv_SE": "Regnbågshastighet", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Швидкість зміни кольорів", "zh_CN": "彩虹滚动速度", "zh_TW": "" } @@ -9192,7 +9342,7 @@ "sv_SE": "", "th_TH": "", "tr_TR": "Esc", - "uk_UA": "", + "uk_UA": "Esc", "zh_CN": "Esc", "zh_TW": "Esc 鍵" } @@ -10867,7 +11017,7 @@ "sv_SE": "", "th_TH": "", "tr_TR": "Rehber", - "uk_UA": "", + "uk_UA": "Меню", "zh_CN": "主页键", "zh_TW": "快顯功能表鍵" } @@ -10892,7 +11042,7 @@ "sv_SE": "Diverse", "th_TH": "", "tr_TR": "Diğer", - "uk_UA": "", + "uk_UA": "Інше", "zh_CN": "截图键", "zh_TW": "其他按鍵" } @@ -11017,7 +11167,7 @@ "sv_SE": "", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Сенсорна панель", "zh_CN": "触摸板", "zh_TW": "觸控板" } @@ -12822,6 +12972,31 @@ "zh_TW": "正在下載更新..." } }, + { + "ID": "DialogRebooterMessage", + "Translations": { + "ar_SA": "من فضلك انتظر، المحاكي في طور إعادة التشغيل", + "de_DE": "Bitte warten Sie, der Emulator wird neu gestartet", + "el_GR": "Παρακαλώ περιμένετε, ο εξομοιωτής επανεκκινείται", + "en_US": "Please wait, the emulator is restarting", + "es_ES": "Por favor, espere, el emulador se está reiniciando", + "fr_FR": "Veuillez patienter, l'émulateur est en train de redémarrer", + "he_IL": "אנא המתן, המחקה מתארגן מחדש", + "it_IT": "Attendere prego, l'emulatore si sta riavviando", + "ja_JP": "お待ちください、エミュレーターが再起動しています", + "ko_KR": "잠시만 기다려 주세요, 에뮬레이터가 재시작 중입니다", + "no_NO": "Vennligst vent, emulatoren starter på nytt", + "pl_PL": "Proszę czekać, emulator jest w trakcie ponownego uruchamiania", + "pt_BR": "Por favor, aguarde, o emulador está reiniciando", + "ru_RU": "Пожалуйста, подождите, эмулятор перезапускается", + "sv_SE": "Vänligen vänta, emulatorn startar om", + "th_TH": "กรุณารอสักครู่, ตัวจำลองกำลังเริ่มใหม่", + "tr_TR": "Lütfen bekleyin, emülatör yeniden başlatılıyor", + "uk_UA": "Будь ласка, зачекайте, емулятор перезавантажується", + "zh_CN": "请稍等,模拟器正在重新启动", + "zh_TW": "請稍候,模擬器正在重新啟動" + } + }, { "ID": "DialogUpdaterExtractionMessage", "Translations": { @@ -13592,7 +13767,7 @@ "sv_SE": "Amiibo-API", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "API Amiibo", "zh_CN": "", "zh_TW": "" } @@ -13839,10 +14014,10 @@ "pl_PL": "", "pt_BR": "Você está prestes a limpar todos os dados PPTC de:\n\n{0}\n\nTem certeza de que deseja continuar?", "ru_RU": "Вы собираетесь удалить все данные PPTC из:\n\n{0}\n\nВы уверены, что хотите продолжить?", - "sv_SE": "", + "sv_SE": "Du är på väg att ta bort allt PPTC-data från:\n\n{0}\n\nÄr du säker på att du vill fortsätta?", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Ви збираєтесь видалити всі дані PPTC з:\n\n{0}\n\nБажаєте продовжити цю операцію?", "zh_CN": "您正要清理 PPTC 数据:\n\n{0}\n\n您确实要继续吗?", "zh_TW": "" } @@ -14142,7 +14317,7 @@ "sv_SE": "En ogiltig nyckelfil hittades i {0}", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Виявлено неправильний файл ключів у теці {0}", "zh_CN": "在 {0} 发现了一个无效的密匙文件", "zh_TW": "找到無效的金鑰檔案 {0}" } @@ -14914,7 +15089,7 @@ "pl_PL": "Wielowątkowość Backendu Graficznego:", "pt_BR": "Multi Enfileiramento do Renderizador Gráfico:", "ru_RU": "Многопоточность графического бэкенда:", - "sv_SE": "Multithreading för grafikbakände:", + "sv_SE": "Multitrådning för grafikbakände:", "th_TH": "มัลติเธรด กราฟิกเบื้องหลัง:", "tr_TR": "Grafik Backend Multithreading:", "uk_UA": "Багатопотоковість графічного сервера:", @@ -15817,7 +15992,7 @@ "sv_SE": "Favorit", "th_TH": "สิ่งที่ชื่นชอบ", "tr_TR": "Favori", - "uk_UA": "Вибрані", + "uk_UA": "Обрані", "zh_CN": "收藏", "zh_TW": "我的最愛" } @@ -16242,7 +16417,7 @@ "sv_SE": "Stöd för direkt musåtkomst (HID). Ger spel åtkomst till din mus som pekdon.\n\nFungerar endast med spel som har inbyggt stöd för muskontroller på Switch-hårdvara, som är endast ett fåtal.\n\nViss pekskärmsfunktionalitet kanske inte fungerar när aktiverat.\n\nLämna AV om du är osäker.", "th_TH": "รองรับการเข้าถึงเมาส์โดยตรง (HID) ให้เกมเข้าถึงเมาส์ของคุณเป็นอุปกรณ์ชี้ตำแหน่ง\n\nใช้งานได้เฉพาะกับเกมที่รองรับการควบคุมเมาส์บนฮาร์ดแวร์ของ Switch เท่านั้น ซึ่งมีอยู่ไม่มากนัก\n\nเมื่อเปิดใช้งาน ฟังก์ชั่นหน้าจอสัมผัสอาจไม่ทำงาน\n\nหากคุณไม่แน่ใจให้ปิดใช้งานไว้", "tr_TR": "", - "uk_UA": "Підтримка прямого доступу до миші (HID). Надає іграм доступ до миші, як пристрій вказування.\n\nПрацює тільки з іграми, які підтримують мишу на обладнанні Switch (таких небагато).\n\nФункціонал сенсорного екрану може не працювати, якщо ця функція ввімкнена.\n\nЗалиште вимкненим, якщо не впевнені.", + "uk_UA": "Підтримка прямого доступу до миші (HID). Надає іграм доступ до миші, як пристрій вказування.\n\nПрацює тільки з тими іграми, що підтримують мишу на обладнанні Switch (таких небагато).\n\nФункціонал сенсорного екрану може не працювати, якщо увімкнути цю функцію.\n\nЗалиште вимкненим, якщо не впевнені.", "zh_CN": "直接鼠标访问(HID)支持,游戏可以直接访问鼠标作为指针输入设备。\n\n只适用于在 Switch 硬件上原生支持鼠标控制的游戏,这种游戏很少。\n\n启用后,触屏功能可能无法正常工作。\n\n如果不确定,请保持关闭状态。", "zh_TW": "支援滑鼠直接存取 (HID)。遊戲可將滑鼠作為指向裝置使用。\n\n僅適用於在 Switch 硬體上原生支援滑鼠控制的遊戲,這類遊戲很少。\n\n啟用後,觸控螢幕功能可能無法使用。\n\n如果不確定,請保持關閉狀態。" } @@ -16667,7 +16842,7 @@ "sv_SE": "Ignorerar Horizon OS-tjänster som inte har implementerats. Detta kan avhjälpa krascher när vissa spel startar upp.\n\nLämna AV om du är osäker.", "th_TH": "ละเว้นบริการ Horizon OS ที่ยังไม่ได้ใช้งาน วิธีนี้อาจช่วยในการหลีกเลี่ยงข้อผิดพลาดเมื่อบูตเกมบางเกม\n\nปล่อยให้ปิดหากคุณไม่แน่ใจ", "tr_TR": "Henüz programlanmamış Horizon işletim sistemi servislerini görmezden gelir. Bu seçenek belirli oyunların açılırken çökmesinin önüne geçmeye yardımcı olabilir.\n\nEmin değilseniz devre dışı bırakın.", - "uk_UA": "Ігнорує нереалізовані служби Horizon OS. Це може допомогти в обході збоїв під час завантаження певних ігор.\n\nЗалиште вимкненим, якщо не впевнені.", + "uk_UA": "Ігнорує нереалізовані служби Horizon OS. Це може допомогти в обході збоїв під час завантаження певних ігор.\n\nЗалиште вимкненим якщо не впевнені.", "zh_CN": "开启后,游戏会忽略未实现的系统服务,从而继续运行。\n少部分新发布的游戏由于使用了新的未知系统服务,可能需要此选项来避免闪退。\n模拟器更新完善系统服务之后,则无需开启此选项。\n\n如果不确定,请保持关闭状态。", "zh_TW": "忽略未實現的 Horizon OS 服務。這可能有助於在啟動某些遊戲時避免崩潰。\n\n如果不確定,請保持關閉狀態。" } @@ -16689,11 +16864,11 @@ "pl_PL": "", "pt_BR": "A caixa de diálogo do Applet do controlador não aparecerá se o controle for desconectado enquanto um aplicativo estiver em execução.\n\nDeixe a opção DESLIGADO se não tiver certeza.", "ru_RU": "Диалоговое окно апплета контроллера не будет отображаться, если геймпад отключен во время работы приложения.\n\nОставьте выключенным, если не уверены.", - "sv_SE": "", + "sv_SE": "Handkontroller-appleten kommer inte att visas om gamepaden är frånkopplad under tiden en applikation körs.\n\nLämna AV om du är osäker.", "th_TH": "", "tr_TR": "", - "uk_UA": "", - "zh_CN": "", + "uk_UA": "Діалогове вікно Аплету Контролера не з'явиться, якщо геймпад було відключено під час роботи програми.\n\nЗалиште вимкненим якщо не впевнені.", + "zh_CN": "在应用程序运行时如果游戏手柄断开连接则不会显示控制器小程序对话框。\n\n如果不确定,请保持关闭状态。", "zh_TW": "" } }, @@ -16717,7 +16892,7 @@ "sv_SE": "Kör kommandon för grafikbakände i en andra tråd.\n\nSnabbar upp shader compilation, minskar stuttering och förbättrar prestandan på GPU-drivrutiner utan stöd för egen multithreading. Något bättre prestanda på drivrutiner med multithreading.\n\nStäll in till AUTO om du är osäker.", "th_TH": "ดำเนินการคำสั่งแบ็กเอนด์กราฟิกบนเธรดที่สอง\n\nเร่งความเร็วการคอมไพล์ ลดการกระตุก และปรับปรุงประสิทธิภาพการทำงานของไดรเวอร์ GPU โดยไม่ต้องรองรับมัลติเธรดในตัว ประสิทธิภาพที่ดีขึ้นเล็กน้อยสำหรับไดรเวอร์ที่มีมัลติเธรด\n\nตั้งเป็น อัตโนมัติ หากคุณไม่แน่ใจ", "tr_TR": "Grafik arka uç komutlarını ikinci bir iş parçacığında işletir.\n\nKendi multithreading desteği olmayan sürücülerde shader derlemeyi hızlandırıp performansı artırır. Multithreading desteği olan sürücülerde çok az daha iyi performans sağlar.\n\nEmin değilseniz Otomatik seçeneğine ayarlayın.", - "uk_UA": "Виконує команди графічного сервера в другому потоці.\n\nПрискорює компіляцію шейдерів, зменшує затримки та покращує продуктивність драйверів GPU без власної підтримки багатопоточності. Трохи краща продуктивність на драйверах з багатопотоковістю.\nВстановіть значення «Авто», якщо не впевнені", + "uk_UA": "Виконує команди графічного сервера в другому потоці.\n\nПрискорює компіляцію шейдерів, зменшує затримки та покращує продуктивність драйверів GPU без власної підтримки багатопоточності. Трохи краща продуктивність на драйверах з багатопотоковістю.\nВстановіть значення «Авто» якщо не впевнені", "zh_CN": "在第二个线程上执行图形引擎指令。\n\n可以加速着色器编译,减少卡顿,提高 GPU 的性能。\n\n如果不确定,请设置为“自动”。", "zh_TW": "在第二個執行緒上執行圖形後端指令。\n\n在本身不支援多執行緒的 GPU 驅動程式上,可加快著色器編譯、減少卡頓並提高效能。在支援多執行緒的驅動程式上效能略有提升。\n\n如果不確定,請設定為自動。" } @@ -16742,7 +16917,7 @@ "sv_SE": "Kör kommandon för grafikbakände i en andra tråd.\n\nSnabbar upp shader compilation, minskar stuttering och förbättrar prestandan på GPU-drivrutiner utan stöd för egen multithreading. Något bättre prestanda på drivrutiner med multithreading.\n\nStäll in till AUTO om du är osäker.", "th_TH": "ดำเนินการคำสั่งแบ็กเอนด์กราฟิกบนเธรดที่สอง\n\nเร่งความเร็วการคอมไพล์เชเดอร์ ลดการกระตุก และปรับปรุงประสิทธิภาพการทำงานของไดรเวอร์ GPU โดยไม่ต้องรองรับมัลติเธรดในตัว ประสิทธิภาพที่ดีขึ้นเล็กน้อยสำหรับไดรเวอร์ที่มีมัลติเธรด\n\nตั้งเป็น อัตโนมัติ หากคุณไม่แน่ใจ", "tr_TR": "Grafik arka uç komutlarını ikinci bir iş parçacığında işletir.\n\nKendi multithreading desteği olmayan sürücülerde shader derlemeyi hızlandırıp performansı artırır. Multithreading desteği olan sürücülerde çok az daha iyi performans sağlar.\n\nEmin değilseniz Otomatik seçeneğine ayarlayın.", - "uk_UA": "Виконує команди графічного сервера в другому потоці.\n\nПрискорює компіляцію шейдерів, зменшує затримки та покращує продуктивність драйверів GPU без власної підтримки багатопоточності. Трохи краща продуктивність на драйверах з багатопотоковістю.\n\nВстановіть значення «Авто», якщо не впевнені.", + "uk_UA": "Виконує команди графічного сервера в другому потоці.\n\nПрискорює компіляцію шейдерів, зменшує затримки та покращує продуктивність драйверів GPU без власної підтримки багатопоточності. Трохи краща продуктивність на драйверах з багатопотоковістю.\n\nВстановіть значення «Авто» якщо не впевнені.", "zh_CN": "在第二个线程上执行图形引擎指令。\n\n可以加速着色器编译,减少卡顿,提高 GPU 的性能。\n\n如果不确定,请设置为“自动”。", "zh_TW": "在第二個執行緒上執行圖形後端指令。\n\n在本身不支援多執行緒的 GPU 驅動程式上,可加快著色器編譯、減少卡頓並提高效能。在支援多執行緒的驅動程式上效能略有提升。\n\n如果不確定,請設定為自動。" } @@ -16767,7 +16942,7 @@ "sv_SE": "Sparar en disk shader cache som minskar stuttering i efterföljande körningar.\n\nLämna PÅ om du är osäker.", "th_TH": "บันทึกแคชแสงเงาของดิสก์ซึ่งช่วยลดการกระตุกในการรันครั้งต่อๆ ไป\n\nเปิดทิ้งไว้หากคุณไม่แน่ใจ", "tr_TR": "Sonraki çalışmalarda takılmaları engelleyen bir gölgelendirici disk önbelleğine kaydeder.", - "uk_UA": "Зберігає кеш дискового шейдера, що зменшує затримки під час наступних запусків.\n\nЗалиште увімкненим, якщо не впевнені.", + "uk_UA": "Зберігає кеш дискового шейдера, що зменшує затримки під час наступних запусків.\n\nЗалиште увімкненим якщо не впевнені.", "zh_CN": "模拟器将已编译的着色器保存到硬盘,可以减少游戏再次渲染相同图形导致的卡顿。\n\n如果不确定,请保持开启状态。", "zh_TW": "儲存磁碟著色器快取,減少後續執行時的卡頓。\n\n如果不確定,請保持開啟狀態。" } @@ -17117,7 +17292,7 @@ "sv_SE": "Aktiverar loggutdata för filsystemsåtkomst i konsollen. Möjliga lägen är 0-3", "th_TH": "เปิดใช้งาน เอาต์พุตประวัติการเข้าถึง FS ไปยังคอนโซล โหมดที่เป็นไปได้คือ 0-3", "tr_TR": "Konsola FS erişim loglarının yazılmasını etkinleştirir. Kullanılabilir modlar 0-3'tür", - "uk_UA": "Вмикає виведення журналу доступу (access log) до FS на консоль. Можливі режими 0-3", + "uk_UA": "Увімкнути виведення журналу доступу (access log) до файлової системи в консоль. Можливі режими: 0-3", "zh_CN": "在控制台中显示文件系统访问日志,可选模式为 0-3。", "zh_TW": "啟用檔案系統存取日誌輸出到控制台中。可能的模式為 0 到 3" } @@ -17164,10 +17339,10 @@ "pl_PL": "", "pt_BR": "Imprime mensagens de log do Avalonia (UI) no console.", "ru_RU": "Выводит сообщения журнала Avalonia (интерфейс) в консоли.", - "sv_SE": "", + "sv_SE": "Skriver ut loggmeddelanden från Avalonia (användargränssnittet) i konsollen.", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Виводити повідомлення журналу Avalonia (UI) в консоль", "zh_CN": "在控制台显示 Avalonia (UI) 的日志信息", "zh_TW": "" } @@ -17317,7 +17492,7 @@ "sv_SE": "Öppna en filutforskare för att välja en eller flera mappar att läsa in alla titeluppdateringar från", "th_TH": "เปิดตัวสำรวจไฟล์เพื่อเลือกหนึ่งโฟลเดอร์ขึ้นไปเพื่อโหลดไฟล์อัปเดตจำนวนมาก", "tr_TR": "", - "uk_UA": "Відкриває Файловий провідник для обрання однієї або декількох тек для масового завантаження оновлень", + "uk_UA": "Відкриває Файловий Провідник для обрання однієї або декількох тек для масового завантаження оновлень", "zh_CN": "打开文件资源管理器以选择一个或多个文件夹来批量加载游戏更新。", "zh_TW": "開啟檔案總管,選擇一個或多個資料夾來大量載入遊戲更新" } @@ -17364,11 +17539,11 @@ "pl_PL": "", "pt_BR": "Abre a pasta de capturas de tela do Ryujinx", "ru_RU": "Открывает папку скриншотов Ryujinx", - "sv_SE": "", + "sv_SE": "Öppna Ryujinx skärmbildsmapp", "th_TH": "", "tr_TR": "", - "uk_UA": "", - "zh_CN": "", + "uk_UA": "Відкрити теку куди зберігаються скріншоти Ryujinx", + "zh_CN": "打开 Ryujinx 截图文件夹", "zh_TW": "" } }, @@ -17392,7 +17567,7 @@ "sv_SE": "Öppnar mappen där loggarna har skrivits till", "th_TH": "เปิดโฟลเดอร์ ที่เก็บไฟล์ประวัติ", "tr_TR": "Log dosyalarının bulunduğu klasörü açar", - "uk_UA": "Відкриває теку, куди записуються журнали (logs)", + "uk_UA": "Відкриває теку, куди зберігаються журнали (logs)", "zh_CN": "打开日志存放的目录", "zh_TW": "開啟日誌被寫入的資料夾" } @@ -17417,7 +17592,7 @@ "sv_SE": "Avsluta Ryujinx", "th_TH": "ออกจากโปรแกรม Ryujinx", "tr_TR": "Ryujinx'ten çıkış yapmayı sağlar", - "uk_UA": "Виходить з Ryujinx", + "uk_UA": "Закриває Ryujinx", "zh_CN": "退出 Ryujinx 模拟器", "zh_TW": "結束 Ryujinx" } @@ -17692,7 +17867,7 @@ "sv_SE": "Ändra ljudvolym", "th_TH": "ปรับระดับเสียง", "tr_TR": "Ses seviyesini değiştirir", - "uk_UA": "Змінити гучність звуку", + "uk_UA": "Регулювання гучності", "zh_CN": "调节音量", "zh_TW": "調節音量" } @@ -18114,10 +18289,10 @@ "pl_PL": "", "pt_BR": "Atualização Disponível!", "ru_RU": "Доступно обновление!", - "sv_SE": "", + "sv_SE": "Uppdatering finns tillgänglig!", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Доступне оновлення!", "zh_CN": "有可用的更新!", "zh_TW": "" } @@ -18714,7 +18889,7 @@ "pl_PL": "", "pt_BR": "Não Reduzido", "ru_RU": "Не обрезан", - "sv_SE": "Inte optimerad", + "sv_SE": "Orörd", "th_TH": "", "tr_TR": "", "uk_UA": "Необрізані", @@ -18992,7 +19167,7 @@ "sv_SE": "Cabinet-dialog", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Сповіщення Cabinet", "zh_CN": "档案对话框", "zh_TW": "Cabinet 對話方塊" } @@ -19842,7 +20017,7 @@ "sv_SE": "Alla tangentbord", "th_TH": "คีย์บอร์ดทั้งหมด", "tr_TR": "Tüm Klavyeler", - "uk_UA": "Всі клавіатури", + "uk_UA": "Усі клавіатури", "zh_CN": "所有键盘", "zh_TW": "所有鍵盤" } @@ -19947,6 +20122,31 @@ "zh_TW": "{0} 更新程式" } }, + { + "ID": "RyujinxRebooter", + "Translations": { + "ar_SA": "إعادة تشغيل {0}", + "de_DE": "Neustart von {0}", + "el_GR": "Επανεκκίνηση {0}", + "en_US": "{0} Reboot", + "es_ES": "Reinicio de {0}", + "fr_FR": "Redémarrage de {0}", + "he_IL": "אתחול {0}", + "it_IT": "Riavvio di {0}", + "ja_JP": "{0} 再起動", + "ko_KR": "{0} 재부팅", + "no_NO": "Omstart av {0}", + "pl_PL": "Ponowne uruchomienie {0}", + "pt_BR": "Reinício de {0}", + "ru_RU": "{0} Перезагрузка", + "sv_SE": "Ominläsning av {0}", + "th_TH": "เริ่มต้นใหม่ {0}", + "tr_TR": "{0} Yeniden Başlatma", + "uk_UA": "Перезавантаження {0}", + "zh_CN": "{0} 重启", + "zh_TW": "{0} 重新啟動" + } + }, { "ID": "SettingsTabHotkeys", "Translations": { @@ -19967,7 +20167,7 @@ "sv_SE": "Snabbtangenter för tangentbord", "th_TH": "ปุ่มลัดของคีย์บอร์ด", "tr_TR": "Klavye Kısayolları", - "uk_UA": "Гарячі клавіші клавіатури", + "uk_UA": "Гарячі клавіші", "zh_CN": "快捷键", "zh_TW": "鍵盤快速鍵" } @@ -20167,7 +20367,7 @@ "sv_SE": "LED-inställningar", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Налаштування LED", "zh_CN": "LED 设置", "zh_TW": "" } @@ -20263,7 +20463,7 @@ "no_NO": "", "pl_PL": "", "pt_BR": "", - "ru_RU": "", + "ru_RU": "Амибо", "sv_SE": "", "th_TH": "", "tr_TR": "", @@ -21117,7 +21317,7 @@ "sv_SE": "Markera visade", "th_TH": "", "tr_TR": "", - "uk_UA": "Вибрати показане", + "uk_UA": "Вибрати показані", "zh_CN": "选定显示的", "zh_TW": "選擇已顯示" } @@ -21967,7 +22167,7 @@ "sv_SE": "Automatiskt", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Автоматично", "zh_CN": "自动", "zh_TW": "自動" } @@ -21992,7 +22192,7 @@ "sv_SE": "Använder Vulkan.\nPå en ARM Mac och vid spel som körs bra på den så används Metal-bakänden.", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Використовує Vulkan.\nНа Mac з ARM-архітектурою, якщо гра добре працює з Vulkan, використовується графічний рушій Metal.", "zh_CN": "使用 Vulkan。\n在 ARM Mac 上,当玩在其下运行良好的游戏时,使用 Metal 后端。", "zh_TW": "使用Vulkan。\n在 ARM Mac 上,如果遊戲執行性能良好時,則將使用 Metal 後端。" } @@ -22692,7 +22892,7 @@ "sv_SE": "Tillämpar anti-aliasing på spelrenderaren.\n\nFXAA kommer att sudda det mesta av bilden, medan SMAA kommer att försöka hitta taggiga kanter och släta ut dem.\n\nRekommenderas inte att använda tillsammans med skalfiltret FSR.\n\nDet här alternativet kan ändras medan ett spel körs genom att klicka på \"Tillämpa\" nedan. Du kan helt enkelt flytta inställningsfönstret åt sidan och experimentera tills du hittar ditt föredragna utseende för ett spel.\n\nLämna som INGEN om du är osäker.", "th_TH": "ใช้การลดรอยหยักกับการเรนเดอร์เกม\n\nFXAA จะเบลอภาพส่วนใหญ่ ในขณะที่ SMAA จะพยายามค้นหารอยหยักและปรับให้เรียบ\n\nไม่แนะนำให้ใช้ร่วมกับตัวกรองสเกล FSR\n\nตัวเลือกนี้สามารถเปลี่ยนแปลงได้ในขณะที่เกมกำลังทำงานอยู่โดยคลิก \"นำไปใช้\" ด้านล่าง คุณสามารถย้ายหน้าต่างการตั้งค่าไปด้านข้างและทดลองจนกว่าคุณจะพบรูปลักษณ์ที่คุณต้องการสำหรับเกม\n\nปล่อยไว้ที่ NONE หากไม่แน่ใจ", "tr_TR": "", - "uk_UA": "Застосовує згладження до рендера гри.\n\nFXAA розмиє більшість зображення, а SMAA спробує знайти нерівні краї та згладити їх.\n\nНе рекомендується використовувати разом з фільтром масштабування FSR.\n\nЦю опцію можна міняти коли гра запущена кліком на \"Застосувати; ви можете відсунути вікно налаштувань і поекспериментувати з видом гри.\n\nЗалиште на \"Немає\", якщо не впевнені.", + "uk_UA": "Застосовує згладження до рендера гри.\n\nFXAA розмиє більшість зображення, а SMAA спробує знайти нерівні краї та згладити їх.\n\nНе рекомендується використовувати разом з фільтром масштабування FSR.\n\nЦю опцію можна міняти коли гра запущена кліком на \"Застосувати; ви можете відсунути вікно налаштувань і поекспериментувати з видом гри.\n\nЗалиште \"Немає\", якщо не впевнені.", "zh_CN": "抗锯齿是一种图形处理技术,用于减少图像边缘的锯齿状现象,使图像更加平滑。\n\nFXAA(快速近似抗锯齿)是一种性能开销相对较小的抗锯齿方法,但可能会使得整体图像看起来有些模糊。\n\nSMAA(增强型子像素抗锯齿)则更加精细,它会尝试找到锯齿边缘并平滑它们,相比 FXAA 有更好的图像质量,但性能开销可能会稍大一些。\n\n如果开启了 FSR(FidelityFX Super Resolution,超级分辨率锐画技术)来提高性能或图像质量,不建议再启用抗锯齿,因为它们会产生不必要的图形处理开销,或者相互之间效果不协调。\n\n在游戏运行时,通过点击下面的“应用”按钮可以使设置生效;你可以将设置窗口移开,并试验找到您喜欢的游戏画面效果。\n\n如果不确定,请保持为“无”。", "zh_TW": "對遊戲繪製進行反鋸齒處理。\n\nFXAA 會模糊大部分圖像,而 SMAA 則會嘗試找出鋸齒邊緣並將其平滑化。\n\n不建議與 FSR 縮放濾鏡一起使用。\n\n此選項可在遊戲執行時透過點選下方的「套用」進行變更;您只需將設定視窗移到一旁,然後進行試驗,直到找到您喜歡的遊戲效果。\n\n如果不確定,請選擇無狀態。" } @@ -22767,7 +22967,7 @@ "sv_SE": "Välj det skalfilter som ska tillämpas vid användning av upplösningsskala.\n\nBilinjär fungerar bra för 3D-spel och är ett säkert standardalternativ.\n\nNärmast rekommenderas för pixel art-spel.\n\nFSR 1.0 är bara ett skarpningsfilter, rekommenderas inte för FXAA eller SMAA.\n\nOmrådesskalning rekommenderas vid nedskalning av upplösning som är större än utdatafönstret. Det kan användas för att uppnå en supersamplad anti-alias-effekt vid nedskalning med mer än 2x.\n\nDetta alternativ kan ändras medan ett spel körs genom att klicka på \"Tillämpa\" nedan. du kan helt enkelt flytta inställningsfönstret åt sidan och experimentera tills du hittar ditt föredragna utseende för ett spel.\n\nLämna som BILINJÄR om du är osäker.", "th_TH": "เลือกตัวกรองสเกลที่จะใช้เมื่อใช้สเกลความละเอียด\n\nBilinear ทำงานได้ดีกับเกม 3D และเป็นตัวเลือกเริ่มต้นที่ปลอดภัย\n\nแนะนำให้ใช้เกมภาพพิกเซลที่ใกล้เคียงที่สุด\n\nFSR 1.0 เป็นเพียงตัวกรองความคมชัด ไม่แนะนำให้ใช้กับ FXAA หรือ SMAA\n\nตัวเลือกนี้สามารถเปลี่ยนแปลงได้ในขณะที่เกมกำลังทำงานอยู่โดยคลิก \"นำไปใช้\" ด้านล่าง คุณสามารถย้ายหน้าต่างการตั้งค่าไปด้านข้างและทดลองจนกว่าคุณจะพบรูปลักษณ์ที่คุณต้องการสำหรับเกม", "tr_TR": "Choose the scaling filter that will be applied when using resolution scale.\n\nBilinear works well for 3D games and is a safe default option.\n\nNearest is recommended for pixel art games.\n\nFSR 1.0 is merely a sharpening filter, not recommended for use with FXAA or SMAA.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on BILINEAR if unsure.", - "uk_UA": "Виберіть фільтр масштабування, що використається при збільшенні роздільної здатності.\n\n\"Білінійний\" добре виглядає в 3D іграх, і хороше налаштування за умовчуванням.\n\n\"Найближчий\" рекомендується для ігор з піксель-артом.\n\n\"FSR 1.0\" - фільтр різкості. Не варто використовувати разом з FXAA або SMAA.\n\nЦю опцію можна змінювати під час гри кліком на \"Застосувати\" нижче; ви можете відсунути вікно налаштувань і поекспериментувати з тим, як відображатиметься гра.\n\nЗалиште на \"Білінійний\", якщо не впевнені.", + "uk_UA": "Виберіть фільтр масштабування, що використається при збільшенні роздільної здатності.\n\n\"Білінійний\" добре виглядає в 3D іграх, і хороше налаштування за умовчуванням.\n\n\"Найближчий\" рекомендується для ігор з піксель-артом.\n\n\"FSR 1.0\" - фільтр різкості. Не варто використовувати разом з FXAA або SMAA.\n\nЦю опцію можна змінювати під час гри кліком на \"Застосувати\" нижче; ви можете відсунути вікно налаштувань і поекспериментувати з тим, як відображатиметься гра.\n\nЗалиште \"Білінійний\", якщо не впевнені.", "zh_CN": "选择在分辨率缩放时将使用的缩放过滤器。\n\nBilinear(双线性过滤)对于3D游戏效果较好,是一个安全的默认选项。\n\nNearest(最近邻过滤)推荐用于像素艺术游戏。\n\nFSR(超级分辨率锐画)只是一个锐化过滤器,不推荐与 FXAA 或 SMAA 抗锯齿一起使用。\n\nArea(局部过滤),当渲染分辨率大于窗口实际分辨率,推荐该选项。该选项在渲染比例大于2.0的情况下,可以实现超采样的效果。\n\n在游戏运行时,通过点击下面的“应用”按钮可以使设置生效;你可以将设置窗口移开,并试验找到您喜欢的游戏画面效果。\n\n如果不确定,请保持为“Bilinear(双线性过滤)”。", "zh_TW": "選擇使用解析度縮放時套用的縮放過濾器。\n\n雙線性 (Bilinear) 濾鏡適用於 3D 遊戲,是一個安全的預設選項。\n\n建議像素美術遊戲使用近鄰性 (Nearest) 濾鏡。\n\nFSR 1.0 只是一個銳化濾鏡,不建議與 FXAA 或 SMAA 一起使用。\n\n此選項可在遊戲執行時透過點選下方的「套用」進行變更;您只需將設定視窗移到一旁,然後進行試驗,直到找到您喜歡的遊戲效果。\n\n如果不確定,請保持雙線性 (Bilinear) 狀態。" } @@ -23967,7 +24167,7 @@ "sv_SE": "Intervall", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Інтервал", "zh_CN": "间隔", "zh_TW": "間隔" } @@ -24067,11 +24267,36 @@ "sv_SE": "Senast uppdaterad: {0}", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Останнє оновлення: {0}", "zh_CN": "最后更新于: {0}", "zh_TW": "上次更新時間: {0}" } }, + { + "ID": "CompatibilityListTitle", + "Translations": { + "ar_SA": "", + "de_DE": "", + "el_GR": "", + "en_US": "Compatibility List - {0} entries", + "es_ES": "", + "fr_FR": "", + "he_IL": "", + "it_IT": "", + "ja_JP": "", + "ko_KR": "", + "no_NO": "", + "pl_PL": "", + "pt_BR": "", + "ru_RU": "", + "sv_SE": "", + "th_TH": "", + "tr_TR": "", + "uk_UA": "Список сумісності — {0} ігор", + "zh_CN": "兼容性列表 - {0} 条", + "zh_TW": "" + } + }, { "ID": "CompatibilityListWarning", "Translations": { @@ -24092,7 +24317,7 @@ "sv_SE": "Denna kompatibilitetslista kan innehålla utdaterade poster.\nTesta gärna spelen som listas med \"Spelproblem\"-status.", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Цей список сумісності може містити застарілі дані.\nНе відмовляйтеся від тестування ігор що мають статус \"Запускаються\".", "zh_CN": "此兼容性列表可能包含过时的条目。\n不要只测试 \"进入游戏\" 状态的游戏。", "zh_TW": "這個相容性列表可能含有已過時的紀錄。\n敬請繼續測試「大致可遊玩 (Ingame)」狀態的遊戲並回報以更新紀錄。" } @@ -24117,11 +24342,36 @@ "sv_SE": "Sök i kompatibilitetsposter...", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Перевірити сумісність гри...", "zh_CN": "正在搜索兼容性条目...", "zh_TW": "搜尋相容性列表紀錄..." } }, + { + "ID": "CompatibilityListSearchBoxWatermarkWithCount", + "Translations": { + "ar_SA": "", + "de_DE": "", + "el_GR": "", + "en_US": "Search {0} compatibility entries...", + "es_ES": "", + "fr_FR": "", + "he_IL": "", + "it_IT": "", + "ja_JP": "", + "ko_KR": "", + "no_NO": "Søk i {0} kompatibilitetsoppføringer...", + "pl_PL": "", + "pt_BR": "", + "ru_RU": "", + "sv_SE": "", + "th_TH": "", + "tr_TR": "", + "uk_UA": "Шукати серед {0} перевірених ігор...", + "zh_CN": "搜索 {0} 兼容性条目...", + "zh_TW": "" + } + }, { "ID": "CompatibilityListOpen", "Translations": { @@ -24142,7 +24392,7 @@ "sv_SE": "Öppna kompatibilitetslistan", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Відкрити Список Сумісності", "zh_CN": "打开兼容性列表", "zh_TW": "開啟相容性列表" } @@ -24167,7 +24417,7 @@ "sv_SE": "Visa endast ägda spel", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Показувати лише ігри в наявності", "zh_CN": "仅显示拥有的游戏", "zh_TW": "只顯示已擁有的遊戲" } @@ -24192,7 +24442,7 @@ "sv_SE": "Spelbart", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Справна", "zh_CN": "可游玩", "zh_TW": "可暢順遊玩" } @@ -24217,7 +24467,7 @@ "sv_SE": "Spelproblem", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "З недоліками", "zh_CN": "进入游戏", "zh_TW": "大致可遊玩" } @@ -24242,7 +24492,7 @@ "sv_SE": "Menyer", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Меню", "zh_CN": "菜单", "zh_TW": "只開啟至遊戲開始功能表" } @@ -24267,7 +24517,7 @@ "sv_SE": "Startar", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Запускається", "zh_CN": "启动", "zh_TW": "只能啟動" } @@ -24292,7 +24542,7 @@ "sv_SE": "Ingenting", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Не працює", "zh_CN": "什么都没有", "zh_TW": "無法啟動" } @@ -24314,10 +24564,10 @@ "pl_PL": "", "pt_BR": "Inicializa e roda sem travamentos ou bugs de GPU de qualquer tipo, e em uma velocidade rápida o suficiente para ser aproveitado em um PC comum.", "ru_RU": "Запускается и работает без любого рода сбоев или графисечких ошибок и на скорости, достаточной для работы на обычном ПК.", - "sv_SE": "", + "sv_SE": "Startar upp och spelas utan några krascher eller GPU-fel av några slag och med en hastighet som är snabb nog för bra upplevelse på en genomsnittlig PC.", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Запускається та оптимально працює (без збоїв або графічних багів) на середньостатистичному комп'ютері.", "zh_CN": "启动和游戏时不会出现任何崩溃或任何类型的 GPU bug 且速度足够快可以在一般 PC 上尽情游玩。", "zh_TW": "" } @@ -24339,10 +24589,10 @@ "pl_PL": "", "pt_BR": "Inicializa e entra no jogo, mas sofre de um ou mais dos seguintes: travamentos, deadlocks, bugs de GPU, áudio ruim que distrai ou é simplesmente muito lento. O jogo ainda pode ser jogado até o fim, mas não da forma como foi criado para ser jogado.", "ru_RU": "Запускается и работает, но возникает одна или несколько из следующих проблем: сбои, взаимоблокировки, ошибки GPU, отвлекающие звуки или просто слишком медленная работа. Возможно, игру всё же удастся пройти до конца, но не так, как она задумана.", - "sv_SE": "", + "sv_SE": "Startar och går in i spelet men lider av ett eller flera av följande: kraschar, deadlocks, GPU-buggar, distraherande dåligt ljud eller är helt enkelt för långsamt. Spelet kan fortfarande spelas hela vägen igenom, men inte så som spelet är avsett att spelas.", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Запускається, але в грі на вас чекатимуть одна або декілька наступних проблем: збої, зависання, графічні баги, спотворений звук або ж гра загалом працюватиме надто повільно. Можливо, її все ще можна пройти, але досвід буде не найкращим.", "zh_CN": "可以成功启动并进入游戏但可能会遇到以下一种或多种问题: 崩溃、卡死、GPU bug、令人无法接受的音频,或者只是太慢。仍然可以继续进行游戏,但是可能无法达到预期。", "zh_TW": "" } @@ -24364,10 +24614,10 @@ "pl_PL": "", "pt_BR": "Inicializa e passa da tela de título, mas não entra no jogo principal.", "ru_RU": "Загружается титульный экран и можно перейти дальше, но сама игра не работает.", - "sv_SE": "", + "sv_SE": "Startar upp och går förbi titelskärmen men tar sig inte in i huvudspelet.", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Запускається та проходить початковий екран, але пограти не вийде.", "zh_CN": "可以启动并通过标题画面但是无法进入到主要的游戏流程。", "zh_TW": "" } @@ -24389,10 +24639,10 @@ "pl_PL": "", "pt_BR": "Inizializa, mas não passa da tela de título.", "ru_RU": "Загружается, но не проходит дальше титульного экрана.", - "sv_SE": "", + "sv_SE": "Startar upp men tar sig inte förbi titelskärmen.", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Запускається, але не відображає навіть початкового екрану.", "zh_CN": "可以启动但是无法通过标题画面。", "zh_TW": "" } @@ -24414,11 +24664,61 @@ "pl_PL": "", "pt_BR": "Não inicializa ou não mostra sinais de atividade.", "ru_RU": "Не запускается или не подаёт признаков жизни.", + "sv_SE": "Startar inte upp eller visar någon form av aktivitet.", + "th_TH": "", + "tr_TR": "", + "uk_UA": "Взагалі не запускається.", + "zh_CN": "无法启动或显示无任何动静。", + "zh_TW": "" + } + }, + { + "ID": "GameSpecificConfigurationHeader", + "Translations": { + "ar_SA": "", + "de_DE": "", + "el_GR": "", + "en_US": "Custom Config", + "es_ES": "", + "fr_FR": "", + "he_IL": "", + "it_IT": "", + "ja_JP": "", + "ko_KR": "", + "no_NO": "", + "pl_PL": "", + "pt_BR": "", + "ru_RU": "", "sv_SE": "", "th_TH": "", "tr_TR": "", - "uk_UA": "", - "zh_CN": "无法启动或显示无任何动静。", + "uk_UA": "Власна конфігурація", + "zh_CN": "自定义配置", + "zh_TW": "" + } + }, + { + "ID": "GameSpecificConfigurationGlobal", + "Translations": { + "ar_SA": "", + "de_DE": "", + "el_GR": "", + "en_US": "(Global)", + "es_ES": "", + "fr_FR": "", + "he_IL": "", + "it_IT": "", + "ja_JP": "", + "ko_KR": "", + "no_NO": "", + "pl_PL": "", + "pt_BR": "", + "ru_RU": "", + "sv_SE": "", + "th_TH": "", + "tr_TR": "", + "uk_UA": "(Глобальні)", + "zh_CN": "(全局)", "zh_TW": "" } }, @@ -24442,7 +24742,7 @@ "sv_SE": "Välj en DLC att extrahera", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Оберіть DLC які бажаєте вилучити", "zh_CN": "选择一个要解压的 DLC", "zh_TW": "" } @@ -24464,10 +24764,10 @@ "pl_PL": "", "pt_BR": "Imagem da Presença do Discord", "ru_RU": "Изображение для статуса активности", - "sv_SE": "", + "sv_SE": "Bild för Rich Presence", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Зображення картки активності Discord", "zh_CN": "Rich Presence 图像", "zh_TW": "" } @@ -24489,10 +24789,10 @@ "pl_PL": "", "pt_BR": "Presença Dinâmica do Discord", "ru_RU": "Динамический статус активности", - "sv_SE": "", + "sv_SE": "Dynamisk Rich Presence", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Динамічна картка активності Discord", "zh_CN": "动态 Rich Presence", "zh_TW": "" } diff --git a/src/Ryujinx/Common/ApplicationHelper.cs b/src/Ryujinx/Common/ApplicationHelper.cs index f4f76d0d3..0b81e8cff 100644 --- a/src/Ryujinx/Common/ApplicationHelper.cs +++ b/src/Ryujinx/Common/ApplicationHelper.cs @@ -13,10 +13,10 @@ using LibHac.Tools.Fs; using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem.NcaUtils; using Ryujinx.Ava.Common.Locale; -using Ryujinx.Ava.UI.Controls; +using Ryujinx.Ava.UI.Windows; using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.Utilities; -using Ryujinx.Ava.Utilities.Configuration; +using Ryujinx.Ava.Systems.Configuration; using Ryujinx.Common.Helper; using Ryujinx.Common.Logging; using Ryujinx.HLE.FileSystem; @@ -216,11 +216,7 @@ namespace Ryujinx.Ava.Common return; } - IntegrityCheckLevel checkLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks - ? IntegrityCheckLevel.ErrorOnInvalid - : IntegrityCheckLevel.None; - - (Nca updatePatchNca, _) = mainNca.GetUpdateData(_virtualFileSystem, checkLevel, programIndex, out _); + (Nca updatePatchNca, _) = mainNca.GetUpdateData(_virtualFileSystem, ConfigurationState.Instance.System.IntegrityCheckLevel, programIndex, out _); if (updatePatchNca is not null) { patchNca = updatePatchNca; diff --git a/src/Ryujinx/Common/LocaleManager.cs b/src/Ryujinx/Common/LocaleManager.cs index 4c86a6177..d116fe709 100644 --- a/src/Ryujinx/Common/LocaleManager.cs +++ b/src/Ryujinx/Common/LocaleManager.cs @@ -1,6 +1,7 @@ using Gommon; +using Ryujinx.Ava.Systems; using Ryujinx.Ava.UI.ViewModels; -using Ryujinx.Ava.Utilities.Configuration; +using Ryujinx.Ava.Systems.Configuration; using Ryujinx.Common; using Ryujinx.Common.Utilities; using System; @@ -25,7 +26,21 @@ namespace Ryujinx.Ava.Common.Locale public LocaleManager() { _localeStrings = new Dictionary(); - _dynamicValues = new ConcurrentDictionary(); + _dynamicValues = new ConcurrentDictionary(new Dictionary + { + { LocaleKeys.DialogConfirmationTitle, [RyujinxApp.FullAppName] }, + { LocaleKeys.DialogUpdaterTitle, [RyujinxApp.FullAppName] }, + { LocaleKeys.DialogErrorTitle, [RyujinxApp.FullAppName] }, + { LocaleKeys.DialogWarningTitle, [RyujinxApp.FullAppName] }, + { LocaleKeys.DialogExitTitle, [RyujinxApp.FullAppName] }, + { LocaleKeys.DialogStopEmulationTitle, [RyujinxApp.FullAppName] }, + { LocaleKeys.RyujinxInfo, [RyujinxApp.FullAppName] }, + { LocaleKeys.RyujinxConfirm, [RyujinxApp.FullAppName] }, + { LocaleKeys.RyujinxUpdater, [RyujinxApp.FullAppName] }, + { LocaleKeys.RyujinxRebooter, [RyujinxApp.FullAppName] }, + { LocaleKeys.CompatibilityListSearchBoxWatermarkWithCount, [CompatibilityDatabase.Entries.Length] }, + { LocaleKeys.CompatibilityListTitle, [CompatibilityDatabase.Entries.Length] } + }); Load(); } @@ -44,16 +59,6 @@ namespace Ryujinx.Ava.Common.Locale ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath); } - - SetDynamicValues(LocaleKeys.DialogConfirmationTitle, RyujinxApp.FullAppName); - SetDynamicValues(LocaleKeys.DialogUpdaterTitle, RyujinxApp.FullAppName); - SetDynamicValues(LocaleKeys.DialogErrorTitle, RyujinxApp.FullAppName); - SetDynamicValues(LocaleKeys.DialogWarningTitle, RyujinxApp.FullAppName); - SetDynamicValues(LocaleKeys.DialogExitTitle, RyujinxApp.FullAppName); - SetDynamicValues(LocaleKeys.DialogStopEmulationTitle, RyujinxApp.FullAppName); - SetDynamicValues(LocaleKeys.RyujinxInfo, RyujinxApp.FullAppName); - SetDynamicValues(LocaleKeys.RyujinxConfirm, RyujinxApp.FullAppName); - SetDynamicValues(LocaleKeys.RyujinxUpdater, RyujinxApp.FullAppName); } public string this[LocaleKeys key] diff --git a/src/Ryujinx/Common/Markup/MarkupExtensions.cs b/src/Ryujinx/Common/Markup/MarkupExtensions.cs index 9e8ff00ef..2c8290847 100644 --- a/src/Ryujinx/Common/Markup/MarkupExtensions.cs +++ b/src/Ryujinx/Common/Markup/MarkupExtensions.cs @@ -24,4 +24,17 @@ namespace Ryujinx.Ava.Common.Markup protected override void ConfigureBindingExtension(CompiledBindingExtension bindingExtension) => bindingExtension.Source = LocaleManager.Instance; } + + internal class WindowTitleExtension(LocaleKeys key, bool includeVersion) : BasicMarkupExtension + { + public WindowTitleExtension(LocaleKeys key) : this(key, true) + { + } + + public override string Name => "WindowTitleTranslation"; + protected override string Value => RyujinxApp.FormatTitle(key, includeVersion); + + protected override void ConfigureBindingExtension(CompiledBindingExtension bindingExtension) + => bindingExtension.Source = LocaleManager.Instance; + } } diff --git a/src/Ryujinx/Common/Models/XCITrimmerFileModel.cs b/src/Ryujinx/Common/Models/XCITrimmerFileModel.cs index da59a5d52..cddc5de22 100644 --- a/src/Ryujinx/Common/Models/XCITrimmerFileModel.cs +++ b/src/Ryujinx/Common/Models/XCITrimmerFileModel.cs @@ -1,4 +1,4 @@ -using Ryujinx.Ava.Utilities.AppLibrary; +using Ryujinx.Ava.Systems.AppLibrary; using Ryujinx.Common.Logging; using Ryujinx.Common.Utilities; diff --git a/src/Ryujinx/Common/XCITrimmerLog.cs b/src/Ryujinx/Common/XCITrimmerLog.cs index b661d918d..751831a66 100644 --- a/src/Ryujinx/Common/XCITrimmerLog.cs +++ b/src/Ryujinx/Common/XCITrimmerLog.cs @@ -26,9 +26,9 @@ namespace Ryujinx.Ava.Common internal class TrimmerWindow : Ryujinx.Common.Logging.XCIFileTrimmerLog { - private readonly XCITrimmerViewModel _viewModel; + private readonly XciTrimmerViewModel _viewModel; - public TrimmerWindow(XCITrimmerViewModel viewModel) + public TrimmerWindow(XciTrimmerViewModel viewModel) { _viewModel = viewModel; } diff --git a/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs b/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs index a1088c8c1..b54de0556 100644 --- a/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs +++ b/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs @@ -2,7 +2,8 @@ using DiscordRPC; using LibHac.Tools.FsSystem; using Ryujinx.Audio.Backends.SDL2; using Ryujinx.Ava; -using Ryujinx.Ava.Utilities.Configuration; +using Ryujinx.Ava.Systems; +using Ryujinx.Ava.Systems.Configuration; using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration.Hid; using Ryujinx.Common.Configuration.Hid.Controller; @@ -11,8 +12,6 @@ using Ryujinx.Common.Configuration.Hid.Keyboard; using Ryujinx.Common.Logging; using Ryujinx.Common.Utilities; using Ryujinx.Graphics.GAL; -using Ryujinx.Graphics.GAL.Multithreading; -using Ryujinx.Graphics.Metal; using Ryujinx.Graphics.OpenGL; using Ryujinx.Graphics.Vulkan; using Ryujinx.HLE; @@ -316,58 +315,47 @@ namespace Ryujinx.Headless preferredGpuId); } - if (options.GraphicsBackend == GraphicsBackend.Metal && window is MetalWindow metalWindow && OperatingSystem.IsMacOS()) - { - return new MetalRenderer(metalWindow.GetLayer); - } - return new OpenGLRenderer(); } - - private static Switch InitializeEmulationContext(WindowBase window, IRenderer renderer, Options options) - { - BackendThreading threadingMode = options.BackendThreading; - - bool threadedGAL = threadingMode == BackendThreading.On || (threadingMode == BackendThreading.Auto && renderer.PreferThreading); - - if (threadedGAL) - { - renderer = new ThreadedRenderer(renderer); - } - - HLEConfiguration configuration = new(_virtualFileSystem, - _libHacHorizonManager, - _contentManager, - _accountManager, - new ImmutableArray(), - _userChannelPersistence, - renderer, - new SDL2HardwareDeviceDriver(), - options.DramSize, - window, - options.SystemLanguage, - options.SystemRegion, - options.VSyncMode, - !options.DisableDockedMode, - !options.DisablePTC, - options.EnableInternetAccess, - !options.DisableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None, - options.FsGlobalAccessLogMode, - options.SystemTimeOffset, - options.SystemTimeZone, - options.MemoryManagerMode, - options.IgnoreMissingServices, - options.AspectRatio, - options.AudioVolume, - options.UseHypervisor ?? true, - options.MultiplayerLanInterfaceId, - Common.Configuration.Multiplayer.MultiplayerMode.Disabled, - false, - string.Empty, - string.Empty, - options.CustomVSyncInterval); - - return new Switch(configuration); - } + + private static Switch InitializeEmulationContext(WindowBase window, IRenderer renderer, Options options) => + new( + new HleConfiguration( + options.DramSize, + options.SystemLanguage, + options.SystemRegion, + options.VSyncMode, + new ImmutableArray(), + !options.DisableDockedMode, + !options.DisablePTC, + options.EnableInternetAccess, + !options.DisableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None, + options.FsGlobalAccessLogMode, + options.SystemTimeOffset, + options.SystemTimeZone, + options.MemoryManagerMode, + options.IgnoreMissingServices, + options.AspectRatio, + options.AudioVolume, + options.UseHypervisor ?? true, + options.MultiplayerLanInterfaceId, + Common.Configuration.Multiplayer.MultiplayerMode.Disabled, + false, + string.Empty, + string.Empty, + options.CustomVSyncInterval + ) + .Configure( + _virtualFileSystem, + _libHacHorizonManager, + _contentManager, + _accountManager, + _userChannelPersistence, + renderer.TryMakeThreaded(options.BackendThreading), + new SDL2HardwareDeviceDriver(), + window, + new ImmutableArray() + ) + ); } } diff --git a/src/Ryujinx/Headless/HeadlessRyujinx.cs b/src/Ryujinx/Headless/HeadlessRyujinx.cs index 003b8a31e..f346f1f63 100644 --- a/src/Ryujinx/Headless/HeadlessRyujinx.cs +++ b/src/Ryujinx/Headless/HeadlessRyujinx.cs @@ -1,7 +1,7 @@ using CommandLine; using Gommon; using Ryujinx.Ava; -using Ryujinx.Ava.Utilities.Configuration; +using Ryujinx.Ava.Systems.Configuration; using Ryujinx.Common; using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration.Hid; @@ -358,9 +358,6 @@ namespace Ryujinx.Headless return options.GraphicsBackend switch { GraphicsBackend.Vulkan => new VulkanWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableMouse, options.HideCursorMode, options.IgnoreControllerApplet), - GraphicsBackend.Metal => OperatingSystem.IsMacOS() ? - new MetalWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableKeyboard, options.HideCursorMode, options.IgnoreControllerApplet) : - throw new Exception("Attempted to use Metal renderer on non-macOS platform!"), _ => new OpenGLWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableMouse, options.HideCursorMode, options.IgnoreControllerApplet) }; } diff --git a/src/Ryujinx/Headless/Options.cs b/src/Ryujinx/Headless/Options.cs index 01d5fc270..13dbb811d 100644 --- a/src/Ryujinx/Headless/Options.cs +++ b/src/Ryujinx/Headless/Options.cs @@ -1,6 +1,7 @@ using CommandLine; using Gommon; -using Ryujinx.Ava.Utilities.Configuration; +using Ryujinx.Ava.Systems.Configuration; +using Ryujinx.Ava.Systems.Configuration.System; using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration.Hid; using Ryujinx.HLE; @@ -37,7 +38,7 @@ namespace Ryujinx.Headless EnableInternetAccess = configurationState.System.EnableInternetAccess; if (NeedsOverride(nameof(DisableFsIntegrityChecks))) - DisableFsIntegrityChecks = configurationState.System.EnableFsIntegrityChecks; + DisableFsIntegrityChecks = !configurationState.System.EnableFsIntegrityChecks; if (NeedsOverride(nameof(FsGlobalAccessLogMode))) FsGlobalAccessLogMode = configurationState.System.FsGlobalAccessLogMode; @@ -58,10 +59,10 @@ namespace Ryujinx.Headless DisableDockedMode = !configurationState.System.EnableDockedMode; if (NeedsOverride(nameof(SystemLanguage))) - SystemLanguage = (SystemLanguage)(int)configurationState.System.Language.Value; + SystemLanguage = configurationState.System.Language.Value.ToHLE(); if (NeedsOverride(nameof(SystemRegion))) - SystemRegion = (RegionCode)(int)configurationState.System.Region.Value; + SystemRegion = configurationState.System.Region.Value.ToHLE(); if (NeedsOverride(nameof(SystemTimeZone))) SystemTimeZone = configurationState.System.TimeZone; diff --git a/src/Ryujinx/Headless/Windows/MetalWindow.cs b/src/Ryujinx/Headless/Windows/MetalWindow.cs deleted file mode 100644 index d79bd7938..000000000 --- a/src/Ryujinx/Headless/Windows/MetalWindow.cs +++ /dev/null @@ -1,47 +0,0 @@ -using Ryujinx.Common.Configuration; -using Ryujinx.Input.HLE; -using Ryujinx.SDL2.Common; -using SharpMetal.QuartzCore; -using System.Runtime.Versioning; -using static SDL2.SDL; - -namespace Ryujinx.Headless -{ - [SupportedOSPlatform("macos")] - class MetalWindow : WindowBase - { - private CAMetalLayer _caMetalLayer; - - public CAMetalLayer GetLayer() - { - return _caMetalLayer; - } - - public MetalWindow( - InputManager inputManager, - GraphicsDebugLevel glLogLevel, - AspectRatio aspectRatio, - bool enableMouse, - HideCursorMode hideCursorMode, - bool ignoreControllerApplet) - : base(inputManager, glLogLevel, aspectRatio, enableMouse, hideCursorMode, ignoreControllerApplet) { } - - public override SDL_WindowFlags WindowFlags => SDL_WindowFlags.SDL_WINDOW_METAL; - - protected override void InitializeWindowRenderer() - { - void CreateLayer() - { - _caMetalLayer = new CAMetalLayer(SDL_Metal_GetLayer(SDL_Metal_CreateView(WindowHandle))); - } - - SDL2Driver.MainThreadDispatcher?.Invoke(CreateLayer); - } - - protected override void InitializeRenderer() { } - - protected override void FinalizeWindowRenderer() { } - - protected override void SwapBuffers() { } - } -} diff --git a/src/Ryujinx/Program.cs b/src/Ryujinx/Program.cs index d7987534f..016cc348a 100644 --- a/src/Ryujinx/Program.cs +++ b/src/Ryujinx/Program.cs @@ -5,10 +5,12 @@ using Gommon; using Projektanker.Icons.Avalonia; using Projektanker.Icons.Avalonia.FontAwesome; using Projektanker.Icons.Avalonia.MaterialDesign; +using Ryujinx.Ava.Systems; using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Windows; using Ryujinx.Ava.Utilities; -using Ryujinx.Ava.Utilities.Configuration; +using Ryujinx.Ava.Systems.Configuration; +using Ryujinx.Ava.Systems.Configuration.System; using Ryujinx.Ava.Utilities.SystemInfo; using Ryujinx.Common; using Ryujinx.Common.Configuration; @@ -32,8 +34,10 @@ namespace Ryujinx.Ava public static double DesktopScaleFactor { get; set; } = 1.0; public static string Version { get; private set; } public static string ConfigurationPath { get; private set; } + public static string GlobalConfigurationPath { get; private set; } public static bool PreviewerDetached { get; private set; } public static bool UseHardwareAcceleration { get; private set; } + public static string BackendThreadingArg { get; private set; } [LibraryImport("user32.dll", SetLastError = true)] public static partial int MessageBoxA(nint hWnd, [MarshalAs(UnmanagedType.LPStr)] string text, [MarshalAs(UnmanagedType.LPStr)] string caption, uint type); @@ -156,11 +160,38 @@ namespace Ryujinx.Ava } } + public static string GetDirGameUserConfig(string gameId, bool rememberGlobalDir = false, bool changeFolderForGame = false) + { + if (string.IsNullOrEmpty(gameId)) + { + return ""; + } + + string gameDir = Path.Combine(AppDataManager.GamesDirPath, gameId, ReleaseInformation.ConfigName); + + // Should load with the game if there is a custom setting for the game + if (rememberGlobalDir) + { + GlobalConfigurationPath = ConfigurationPath; + } + + if (changeFolderForGame) + { + ConfigurationPath = gameDir; + } + + return gameDir; + } + public static void ReloadConfig() { + //It is necessary that when a user setting appears, the global setting remains available + GlobalConfigurationPath = null; + string localConfigurationPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ReleaseInformation.ConfigName); string appDataConfigurationPath = Path.Combine(AppDataManager.BaseDirPath, ReleaseInformation.ConfigName); + // Now load the configuration as the other subsystems are now registered if (File.Exists(localConfigurationPath)) { @@ -204,7 +235,6 @@ namespace Ryujinx.Ava { "opengl" => GraphicsBackend.OpenGl, "vulkan" => GraphicsBackend.Vulkan, - "metal" => GraphicsBackend.Metal, _ => ConfigurationState.Instance.Graphics.GraphicsBackend }; @@ -218,6 +248,11 @@ namespace Ryujinx.Ava _ => ConfigurationState.Instance.Graphics.BackendThreading }; + if (CommandLineState.OverrideBackendThreadingAfterReboot is not null) + { + BackendThreadingArg = CommandLineState.OverrideBackendThreadingAfterReboot; + } + // Check if docked mode was overriden. if (CommandLineState.OverrideDockedMode.HasValue) ConfigurationState.Instance.System.EnableDockedMode.Value = CommandLineState.OverrideDockedMode.Value; @@ -233,6 +268,33 @@ namespace Ryujinx.Ava _ => ConfigurationState.Instance.HideCursor, }; + // Check if memoryManagerMode was overridden. + if (CommandLineState.OverrideMemoryManagerMode is not null) + if (Enum.TryParse(CommandLineState.OverrideMemoryManagerMode, true, out MemoryManagerMode result)) + { + ConfigurationState.Instance.System.MemoryManagerMode.Value = result; + } + + // Check if PPTC was overridden. + if (CommandLineState.OverridePPTC is not null) + if (Enum.TryParse(CommandLineState.OverridePPTC, true, out bool result)) + { + ConfigurationState.Instance.System.EnablePtc.Value = result; + } + + // Check if region was overridden. + if (CommandLineState.OverrideSystemRegion is not null) + if (Enum.TryParse(CommandLineState.OverrideSystemRegion, true, out HLE.HOS.SystemState.RegionCode result)) + { + ConfigurationState.Instance.System.Region.Value = result.ToUI(); + } + + //Check if language was overridden. + if (CommandLineState.OverrideSystemLanguage is not null) + if (Enum.TryParse(CommandLineState.OverrideSystemLanguage, true, out HLE.HOS.SystemState.SystemLanguage result)) + { + ConfigurationState.Instance.System.Language.Value = result.ToUI(); + } // Check if hardware-acceleration was overridden. if (CommandLineState.OverrideHardwareAcceleration != null) diff --git a/src/Ryujinx/Ryujinx.csproj b/src/Ryujinx/Ryujinx.csproj index 7bec0d16f..2369352fe 100644 --- a/src/Ryujinx/Ryujinx.csproj +++ b/src/Ryujinx/Ryujinx.csproj @@ -13,10 +13,9 @@ $(DefaultItemExcludes);._* - - - - + + + @@ -48,11 +47,11 @@ + + - - @@ -72,8 +71,6 @@ - - @@ -81,6 +78,7 @@ + @@ -93,10 +91,12 @@ Always THIRDPARTY.md + False Always LICENSE.txt + False @@ -127,6 +127,7 @@ + @@ -172,11 +173,4 @@ - - - SettingsRealApplets.axaml - Code - - - diff --git a/src/Ryujinx/AppHost.cs b/src/Ryujinx/Systems/AppHost.cs similarity index 93% rename from src/Ryujinx/AppHost.cs rename to src/Ryujinx/Systems/AppHost.cs index 033d7e175..209e3cac6 100644 --- a/src/Ryujinx/AppHost.cs +++ b/src/Ryujinx/Systems/AppHost.cs @@ -21,8 +21,8 @@ using Ryujinx.Ava.UI.Renderer; using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.Windows; using Ryujinx.Ava.Utilities; -using Ryujinx.Ava.Utilities.AppLibrary; -using Ryujinx.Ava.Utilities.Configuration; +using Ryujinx.Ava.Systems.AppLibrary; +using Ryujinx.Ava.Systems.Configuration; using Ryujinx.Common; using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration.Multiplayer; @@ -33,7 +33,6 @@ using Ryujinx.Common.Utilities; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL.Multithreading; using Ryujinx.Graphics.Gpu; -using Ryujinx.Graphics.Metal; using Ryujinx.Graphics.OpenGL; using Ryujinx.Graphics.Vulkan; using Ryujinx.HLE; @@ -67,7 +66,7 @@ using Size = Avalonia.Size; using Switch = Ryujinx.HLE.Switch; using VSyncMode = Ryujinx.Common.Configuration.VSyncMode; -namespace Ryujinx.Ava +namespace Ryujinx.Ava.Systems { internal class AppHost { @@ -478,7 +477,7 @@ namespace Ryujinx.Ava Dispatcher.UIThread.InvokeAsync(() => { - _viewModel.Title = TitleHelper.ActiveApplicationTitle(Device.Processes.ActiveApplication, Program.Version, !ConfigurationState.Instance.ShowTitleBar); + _viewModel.Title = TitleHelper.ActiveApplicationTitle(Device.Processes.ActiveApplication, Program.Version, !ConfigurationState.Instance.ShowOldUI); }); _viewModel.SetUiProgressHandlers(Device); @@ -882,7 +881,7 @@ namespace Ryujinx.Ava Device?.System.TogglePauseEmulation(false); _viewModel.IsPaused = false; - _viewModel.Title = TitleHelper.ActiveApplicationTitle(Device?.Processes.ActiveApplication, Program.Version, !ConfigurationState.Instance.ShowTitleBar); + _viewModel.Title = TitleHelper.ActiveApplicationTitle(Device?.Processes.ActiveApplication, Program.Version, !ConfigurationState.Instance.ShowOldUI); Logger.Info?.Print(LogClass.Emulation, "Emulation was resumed"); } @@ -891,7 +890,7 @@ namespace Ryujinx.Ava Device?.System.TogglePauseEmulation(true); _viewModel.IsPaused = true; - _viewModel.Title = TitleHelper.ActiveApplicationTitle(Device?.Processes.ActiveApplication, Program.Version, !ConfigurationState.Instance.ShowTitleBar, LocaleManager.Instance[LocaleKeys.Paused]); + _viewModel.Title = TitleHelper.ActiveApplicationTitle(Device?.Processes.ActiveApplication, Program.Version, !ConfigurationState.Instance.ShowOldUI, LocaleManager.Instance[LocaleKeys.Paused]); Logger.Info?.Print(LogClass.Emulation, "Emulation was paused"); } @@ -901,75 +900,36 @@ namespace Ryujinx.Ava VirtualFileSystem.ReloadKeySet(); // Initialize Renderer. - GraphicsBackend backend = TitleIDs.SelectGraphicsBackend(ApplicationId.ToString("X16"), ConfigurationState.Instance.Graphics.GraphicsBackend); + GraphicsBackend backend = ConfigurationState.Instance.Graphics.GraphicsBackend; IRenderer renderer = backend switch { -#pragma warning disable CA1416 // This call site is reachable on all platforms - // SelectGraphicsBackend does a check for Mac, on top of checking if it's an ARM Mac. This isn't a problem. - GraphicsBackend.Metal => new MetalRenderer((RendererHost.EmbeddedWindow as EmbeddedWindowMetal)!.CreateSurface), -#pragma warning restore CA1416 GraphicsBackend.Vulkan => VulkanRenderer.Create( ConfigurationState.Instance.Graphics.PreferredGpu, (RendererHost.EmbeddedWindow as EmbeddedWindowVulkan)!.CreateSurface, VulkanHelper.GetRequiredInstanceExtensions), _ => new OpenGLRenderer() }; - - BackendThreading threadingMode = ConfigurationState.Instance.Graphics.BackendThreading; - - bool isGALThreaded = threadingMode == BackendThreading.On || (threadingMode == BackendThreading.Auto && renderer.PreferThreading); - if (isGALThreaded) - { - renderer = new ThreadedRenderer(renderer); - } - - Logger.Info?.PrintMsg(LogClass.Gpu, $"Backend Threading ({threadingMode}): {isGALThreaded}"); - - // Initialize Configuration. - MemoryConfiguration memoryConfiguration = ConfigurationState.Instance.System.DramSize.Value; _titles = new List(); foreach (ApplicationData App in _viewModel.Applications) { _titles.Add(new(new ApplicationId(App.Id),App.ControlHolder.Value,App.Path,App.Icon)); } - - Device = new Switch(new HLEConfiguration( - VirtualFileSystem, - _viewModel.LibHacHorizonManager, - ContentManager, - _accountManager, - Titles.ToImmutableList(), - _userChannelPersistence, - renderer, - InitializeAudio(), - memoryConfiguration, - _viewModel.UiHandler, - (SystemLanguage)ConfigurationState.Instance.System.Language.Value, - (RegionCode)ConfigurationState.Instance.System.Region.Value, - ConfigurationState.Instance.Graphics.VSyncMode, - ConfigurationState.Instance.System.EnableDockedMode, - ConfigurationState.Instance.System.EnablePtc, - ConfigurationState.Instance.System.EnableInternetAccess, - ConfigurationState.Instance.System.EnableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None, - ConfigurationState.Instance.System.FsGlobalAccessLogMode, - ConfigurationState.Instance.System.MatchSystemTime - ? 0 - : ConfigurationState.Instance.System.SystemTimeOffset, - ConfigurationState.Instance.System.TimeZone, - ConfigurationState.Instance.System.MemoryManagerMode, - ConfigurationState.Instance.System.IgnoreMissingServices, - ConfigurationState.Instance.Graphics.AspectRatio, - ConfigurationState.Instance.System.AudioVolume, - ConfigurationState.Instance.System.UseHypervisor, - ConfigurationState.Instance.Multiplayer.LanInterfaceId.Value, - ConfigurationState.Instance.Multiplayer.Mode, - ConfigurationState.Instance.Multiplayer.DisableP2p, - ConfigurationState.Instance.Multiplayer.LdnPassphrase, - ConfigurationState.Instance.Multiplayer.LdnServer, - ConfigurationState.Instance.Graphics.CustomVSyncInterval.Value, - ConfigurationState.Instance.Hacks.ShowDirtyHacks ? ConfigurationState.Instance.Hacks.EnabledHacks : null)); + + Device = new Switch(ConfigurationState.Instance.CreateHleConfiguration() + .Configure( + VirtualFileSystem, + _viewModel.LibHacHorizonManager, + ContentManager, + _accountManager, + _userChannelPersistence, + renderer.TryMakeThreaded(ConfigurationState.Instance.Graphics.BackendThreading), + InitializeAudio(), + _viewModel.UiHandler, + _titles.ToImmutableList() + ) + ); } private static IHardwareDeviceDriver InitializeAudio() @@ -1134,6 +1094,13 @@ namespace Ryujinx.Ava }); (RendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.MakeCurrent(true); + + // Reload settings when the game is turned off + // (resets custom settings if there were any) + Program.ReloadConfig(); + + // Reload application list (changes the status of the user setting if it was added or removed during the game) + Dispatcher.UIThread.Post(() => RyujinxApp.MainWindow.LoadApplications()); } public void InitStatus() @@ -1142,7 +1109,6 @@ namespace Ryujinx.Ava { GraphicsBackend.Vulkan => "Vulkan", GraphicsBackend.OpenGl => "OpenGL", - GraphicsBackend.Metal => "Metal", _ => throw new NotImplementedException() }; @@ -1197,6 +1163,9 @@ namespace Ryujinx.Ava private void UpdateShaderCount() { + if (_displayCount is 0 && _renderer.ProgramCount is 0) + return; + // If there is a mismatch between total program compile and previous count // this means new shaders have been compiled and should be displayed. if (_renderer.ProgramCount != _previousCount) @@ -1270,12 +1239,10 @@ namespace Ryujinx.Ava VSyncModeToggle(); break; case KeyboardHotkeyState.CustomVSyncIntervalDecrement: - Device.DecrementCustomVSyncInterval(); - _viewModel.CustomVSyncInterval -= 1; + _viewModel.CustomVSyncInterval = Device.DecrementCustomVSyncInterval(); break; case KeyboardHotkeyState.CustomVSyncIntervalIncrement: - Device.IncrementCustomVSyncInterval(); - _viewModel.CustomVSyncInterval += 1; + _viewModel.CustomVSyncInterval = Device.IncrementCustomVSyncInterval(); break; case KeyboardHotkeyState.Screenshot: ScreenshotRequested = true; diff --git a/src/Ryujinx/Utilities/AppLibrary/ApplicationCountUpdatedEventArgs.cs b/src/Ryujinx/Systems/AppLibrary/ApplicationCountUpdatedEventArgs.cs similarity index 81% rename from src/Ryujinx/Utilities/AppLibrary/ApplicationCountUpdatedEventArgs.cs rename to src/Ryujinx/Systems/AppLibrary/ApplicationCountUpdatedEventArgs.cs index 9bbaef0e3..7866ffa3a 100644 --- a/src/Ryujinx/Utilities/AppLibrary/ApplicationCountUpdatedEventArgs.cs +++ b/src/Ryujinx/Systems/AppLibrary/ApplicationCountUpdatedEventArgs.cs @@ -1,6 +1,6 @@ using System; -namespace Ryujinx.Ava.Utilities.AppLibrary +namespace Ryujinx.Ava.Systems.AppLibrary { public class ApplicationCountUpdatedEventArgs : EventArgs { diff --git a/src/Ryujinx/Utilities/AppLibrary/ApplicationData.cs b/src/Ryujinx/Systems/AppLibrary/ApplicationData.cs similarity index 97% rename from src/Ryujinx/Utilities/AppLibrary/ApplicationData.cs rename to src/Ryujinx/Systems/AppLibrary/ApplicationData.cs index 2658352d7..0819b86b8 100644 --- a/src/Ryujinx/Utilities/AppLibrary/ApplicationData.cs +++ b/src/Ryujinx/Systems/AppLibrary/ApplicationData.cs @@ -9,21 +9,21 @@ using LibHac.Tools.Fs; using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem.NcaUtils; using Ryujinx.Ava.Common.Locale; -using Ryujinx.Ava.Utilities.Compat; -using Ryujinx.Ava.Utilities.PlayReport; +using Ryujinx.Ava.Utilities; +using Ryujinx.Ava.Systems.PlayReport; using Ryujinx.Common.Logging; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.Loaders.Processes.Extensions; using System; using System.IO; -using System.Text; using System.Text.Json.Serialization; -namespace Ryujinx.Ava.Utilities.AppLibrary +namespace Ryujinx.Ava.Systems.AppLibrary { public class ApplicationData { public bool Favorite { get; set; } + public bool HasIndependentConfiguration { get; set; } public byte[] Icon { get; set; } public string Name { get; set; } = "Unknown"; @@ -36,7 +36,7 @@ namespace Ryujinx.Ava.Utilities.AppLibrary { _id = value; - Compatibility = CompatibilityCsv.Find(value); + Compatibility = CompatibilityDatabase.Find(value); RichPresenceSpec = PlayReports.Analyzer.TryGetSpec(IdString, out GameSpec gameSpec) ? gameSpec : default(Optional); diff --git a/src/Ryujinx/Utilities/AppLibrary/ApplicationJsonSerializerContext.cs b/src/Ryujinx/Systems/AppLibrary/ApplicationJsonSerializerContext.cs similarity index 85% rename from src/Ryujinx/Utilities/AppLibrary/ApplicationJsonSerializerContext.cs rename to src/Ryujinx/Systems/AppLibrary/ApplicationJsonSerializerContext.cs index d443ab66e..3fd24a6ba 100644 --- a/src/Ryujinx/Utilities/AppLibrary/ApplicationJsonSerializerContext.cs +++ b/src/Ryujinx/Systems/AppLibrary/ApplicationJsonSerializerContext.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Ryujinx.Ava.Utilities.AppLibrary +namespace Ryujinx.Ava.Systems.AppLibrary { [JsonSourceGenerationOptions(WriteIndented = true)] [JsonSerializable(typeof(ApplicationMetadata))] diff --git a/src/Ryujinx/Utilities/AppLibrary/ApplicationLibrary.cs b/src/Ryujinx/Systems/AppLibrary/ApplicationLibrary.cs similarity index 98% rename from src/Ryujinx/Utilities/AppLibrary/ApplicationLibrary.cs rename to src/Ryujinx/Systems/AppLibrary/ApplicationLibrary.cs index 79cac1a0e..b7321d6c1 100644 --- a/src/Ryujinx/Utilities/AppLibrary/ApplicationLibrary.cs +++ b/src/Ryujinx/Systems/AppLibrary/ApplicationLibrary.cs @@ -12,8 +12,9 @@ using LibHac.Tools.Fs; using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem.NcaUtils; using Ryujinx.Ava.Common.Models; -using Ryujinx.Ava.Utilities.Configuration; -using Ryujinx.Ava.Utilities.Configuration.System; +using Ryujinx.Ava.Utilities; +using Ryujinx.Ava.Systems.Configuration; +using Ryujinx.Ava.Systems.Configuration.System; using Ryujinx.Common; using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration.Multiplayer; @@ -38,11 +39,10 @@ using MissingKeyException = LibHac.Common.Keys.MissingKeyException; using Path = System.IO.Path; using TimeSpan = System.TimeSpan; -namespace Ryujinx.Ava.Utilities.AppLibrary +namespace Ryujinx.Ava.Systems.AppLibrary { public class ApplicationLibrary { - public const string DefaultLanPlayWebHost = "ryuldnweb.vudjun.com"; public Language DesiredLanguage { get; set; } public event EventHandler ApplicationCountUpdated; public event Action LdnGameDataReceived; @@ -505,7 +505,7 @@ namespace Ryujinx.Ava.Utilities.AppLibrary if (data.Id != 0) { ApplicationMetadata appMetadata = LoadAndSaveMetaData(data.IdString, appMetadata => - { + { appMetadata.Title = data.Name; // Only do the migration if time_played has a value and timespan_played hasn't been updated yet. @@ -529,10 +529,11 @@ namespace Ryujinx.Ava.Utilities.AppLibrary } }); - + data.Favorite = appMetadata.Favorite; data.TimePlayed = appMetadata.TimePlayed; data.LastPlayed = appMetadata.LastPlayed; + data.HasIndependentConfiguration = File.Exists(Program.GetDirGameUserConfig(data.IdBaseString, false, false)); // Just check user config } data.FileExtension = Path.GetExtension(applicationPath).TrimStart('.').ToUpper(); @@ -617,15 +618,11 @@ namespace Ryujinx.Ava.Utilities.AppLibrary case ".xci": case ".nsp": { - IntegrityCheckLevel checkLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks - ? IntegrityCheckLevel.ErrorOnInvalid - : IntegrityCheckLevel.None; - using IFileSystem pfs = PartitionFileSystemUtils.OpenApplicationFileSystem(filePath, _virtualFileSystem); Dictionary updates = - pfs.GetContentData(ContentMetaType.Patch, _virtualFileSystem, checkLevel); + pfs.GetContentData(ContentMetaType.Patch, _virtualFileSystem, ConfigurationState.Instance.System.IntegrityCheckLevel); if (updates.Count == 0) { @@ -826,7 +823,6 @@ namespace Ryujinx.Ava.Utilities.AppLibrary public async Task RefreshLdn() { - if (ConfigurationState.Instance.Multiplayer.Mode == MultiplayerMode.LdnRyu) { try @@ -834,33 +830,22 @@ namespace Ryujinx.Ava.Utilities.AppLibrary string ldnWebHost = ConfigurationState.Instance.Multiplayer.LdnServer; if (string.IsNullOrEmpty(ldnWebHost)) { - ldnWebHost = DefaultLanPlayWebHost; + ldnWebHost = SharedConstants.DefaultLanPlayWebHost; } - IEnumerable ldnGameDataArray = Array.Empty(); + using HttpClient httpClient = new(); string ldnGameDataArrayString = await httpClient.GetStringAsync($"https://{ldnWebHost}/api/public_games"); - ldnGameDataArray = JsonHelper.Deserialize(ldnGameDataArrayString, _ldnDataSerializerContext.IEnumerableLdnGameData); - LdnGameDataReceived?.Invoke(new LdnGameDataReceivedEventArgs - { - LdnData = ldnGameDataArray - }); + LdnGameData[] ldnGameDataArray = JsonHelper.Deserialize(ldnGameDataArrayString, _ldnDataSerializerContext.IEnumerableLdnGameData).ToArray(); + LdnGameDataReceived?.Invoke(new LdnGameDataReceivedEventArgs(ldnGameDataArray)); + return; } catch (Exception ex) { Logger.Warning?.Print(LogClass.Application, $"Failed to fetch the public games JSON from the API. Player and game count in the game list will be unavailable.\n{ex.Message}"); - LdnGameDataReceived?.Invoke(new LdnGameDataReceivedEventArgs - { - LdnData = Array.Empty() - }); } } - else - { - LdnGameDataReceived?.Invoke(new LdnGameDataReceivedEventArgs - { - LdnData = Array.Empty() - }); - } + + LdnGameDataReceived?.Invoke(LdnGameDataReceivedEventArgs.Empty); } // Replace the currently stored DLC state for the game with the provided DLC state. diff --git a/src/Ryujinx/Utilities/AppLibrary/ApplicationMetadata.cs b/src/Ryujinx/Systems/AppLibrary/ApplicationMetadata.cs similarity index 97% rename from src/Ryujinx/Utilities/AppLibrary/ApplicationMetadata.cs rename to src/Ryujinx/Systems/AppLibrary/ApplicationMetadata.cs index d823c7482..9d8488aeb 100644 --- a/src/Ryujinx/Utilities/AppLibrary/ApplicationMetadata.cs +++ b/src/Ryujinx/Systems/AppLibrary/ApplicationMetadata.cs @@ -1,7 +1,7 @@ using System; using System.Text.Json.Serialization; -namespace Ryujinx.Ava.Utilities.AppLibrary +namespace Ryujinx.Ava.Systems.AppLibrary { public class ApplicationMetadata { diff --git a/src/Ryujinx/Utilities/AppLibrary/LdnGameData.cs b/src/Ryujinx/Systems/AppLibrary/LdnGameData.cs similarity index 76% rename from src/Ryujinx/Utilities/AppLibrary/LdnGameData.cs rename to src/Ryujinx/Systems/AppLibrary/LdnGameData.cs index 4b9b8fe6c..6750983d6 100644 --- a/src/Ryujinx/Utilities/AppLibrary/LdnGameData.cs +++ b/src/Ryujinx/Systems/AppLibrary/LdnGameData.cs @@ -4,7 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; -namespace Ryujinx.Ava.Utilities.AppLibrary +namespace Ryujinx.Ava.Systems.AppLibrary { public struct LdnGameData { @@ -18,7 +18,7 @@ namespace Ryujinx.Ava.Utilities.AppLibrary public IEnumerable Players { get; set; } public static Array GetArrayForApp( - IEnumerable receivedData, ref ApplicationControlProperty acp) + LdnGameData[] receivedData, ref ApplicationControlProperty acp) { LibHac.Common.FixedArrays.Array8 communicationId = acp.LocalCommunicationId; @@ -40,4 +40,10 @@ namespace Ryujinx.Ava.Utilities.AppLibrary public int GameCount => _ldnDatas.Length; } } + + public static class LdnGameDataHelper + { + public static LdnGameData.Array Where(this LdnGameData[] unfilteredDatas, ref ApplicationControlProperty acp) + => LdnGameData.GetArrayForApp(unfilteredDatas, ref acp); + } } diff --git a/src/Ryujinx/Systems/AppLibrary/LdnGameDataReceivedEventArgs.cs b/src/Ryujinx/Systems/AppLibrary/LdnGameDataReceivedEventArgs.cs new file mode 100644 index 000000000..21d24ff2d --- /dev/null +++ b/src/Ryujinx/Systems/AppLibrary/LdnGameDataReceivedEventArgs.cs @@ -0,0 +1,17 @@ +using System; + +namespace Ryujinx.Ava.Systems.AppLibrary +{ + public class LdnGameDataReceivedEventArgs : EventArgs + { + public static new readonly LdnGameDataReceivedEventArgs Empty = new(null); + + public LdnGameDataReceivedEventArgs(LdnGameData[] ldnData) + { + LdnData = ldnData ?? []; + } + + + public LdnGameData[] LdnData { get; set; } + } +} diff --git a/src/Ryujinx/Utilities/AppLibrary/LdnGameDataSerializerContext.cs b/src/Ryujinx/Systems/AppLibrary/LdnGameDataSerializerContext.cs similarity index 83% rename from src/Ryujinx/Utilities/AppLibrary/LdnGameDataSerializerContext.cs rename to src/Ryujinx/Systems/AppLibrary/LdnGameDataSerializerContext.cs index 90d1894c7..ff7718ed5 100644 --- a/src/Ryujinx/Utilities/AppLibrary/LdnGameDataSerializerContext.cs +++ b/src/Ryujinx/Systems/AppLibrary/LdnGameDataSerializerContext.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using System.Text.Json.Serialization; -namespace Ryujinx.Ava.Utilities.AppLibrary +namespace Ryujinx.Ava.Systems.AppLibrary { [JsonSerializable(typeof(IEnumerable))] internal partial class LdnGameDataSerializerContext : JsonSerializerContext; diff --git a/src/Ryujinx/Utilities/Compat/CompatibilityCsv.cs b/src/Ryujinx/Systems/CompatibilityDatabase.cs similarity index 82% rename from src/Ryujinx/Utilities/Compat/CompatibilityCsv.cs rename to src/Ryujinx/Systems/CompatibilityDatabase.cs index b58c05299..bfc7ba903 100644 --- a/src/Ryujinx/Utilities/Compat/CompatibilityCsv.cs +++ b/src/Ryujinx/Systems/CompatibilityDatabase.cs @@ -9,26 +9,11 @@ using System.Linq; using System.Reflection; using System.Text; -namespace Ryujinx.Ava.Utilities.Compat +namespace Ryujinx.Ava.Systems { - public struct ColumnIndices(Func, int> getIndex) + public class CompatibilityDatabase { - public const string TitleIdCol = "\"title_id\""; - public const string GameNameCol = "\"game_name\""; - public const string LabelsCol = "\"labels\""; - public const string StatusCol = "\"status\""; - public const string LastUpdatedCol = "\"last_updated\""; - - public readonly int TitleId = getIndex(TitleIdCol); - public readonly int GameName = getIndex(GameNameCol); - public readonly int Labels = getIndex(LabelsCol); - public readonly int Status = getIndex(StatusCol); - public readonly int LastUpdated = getIndex(LastUpdatedCol); - } - - public class CompatibilityCsv - { - static CompatibilityCsv() => Load(); + static CompatibilityDatabase() => Load(); public static void Load() { @@ -65,16 +50,6 @@ namespace Ryujinx.Ava.Utilities.Compat public static CompatibilityEntry Find(ulong titleId) => Find(titleId.ToString("X16")); - - public static LocaleKeys? GetStatus(string titleId) - => Find(titleId)?.Status; - - public static LocaleKeys? GetStatus(ulong titleId) => GetStatus(titleId.ToString("X16")); - - public static string GetLabels(string titleId) - => Find(titleId)?.FormattedIssueLabels; - - public static string GetLabels(ulong titleId) => GetLabels(titleId.ToString("X16")); } public class CompatibilityEntry @@ -135,6 +110,7 @@ namespace Ryujinx.Ava.Utilities.Compat public string FormattedIssueLabels => Labels .Select(FormatLabelName) + .Where(x => x != null) .JoinToString(", "); public override string ToString() => @@ -158,7 +134,6 @@ namespace Ryujinx.Ava.Utilities.Compat "gui" => "GUI", "help wanted" => "Help Wanted", "horizon" => "Horizon", - "infra" => "Project Infra", "invalid" => "Invalid", "kernel" => "Kernel", "ldn" => "LDN", @@ -172,9 +147,9 @@ namespace Ryujinx.Ava.Utilities.Compat "ldn-untested" => "LDN Untested", "ldn-broken" => "LDN Broken", "ldn-partial" => "Partial LDN", - "nvdec" => "NVDEC", - "services" => "NX Services", - "services-horizon" => "Horizon OS Services", + "nvdec" => "GPU Video Decoding", + "services" => "HLE Services", + "services-horizon" => "New HLE Services", "slow" => "Runs Slow", "crash" => "Crashes", "deadlock" => "Deadlock", @@ -182,7 +157,7 @@ namespace Ryujinx.Ava.Utilities.Compat "opengl" => "OpenGL", "opengl-backend-bug" => "OpenGL Backend Bug", "vulkan-backend-bug" => "Vulkan Backend Bug", - "mac-bug" => "Mac-specific Bug(s)", + "mac-bug" => "Mac-specific Problems", "amd-vendor-bug" => "AMD GPU Bug", "intel-vendor-bug" => "Intel GPU Bug", "loader-allocator" => "Loader Allocator", @@ -191,18 +166,22 @@ namespace Ryujinx.Ava.Utilities.Compat "UE4" => "Unreal Engine 4", "homebrew" => "Homebrew Content", "online-broken" => "Online Broken", - _ => Capitalize(labelName) + _ => null }; + } + + public struct ColumnIndices(Func, int> getIndex) + { + private const string TitleIdCol = "\"title_id\""; + private const string GameNameCol = "\"game_name\""; + private const string LabelsCol = "\"labels\""; + private const string StatusCol = "\"status\""; + private const string LastUpdatedCol = "\"last_updated\""; - public static string Capitalize(string value) - { - if (value == string.Empty) - return string.Empty; - - char firstChar = value[0]; - string rest = value[1..]; - - return $"{char.ToUpper(firstChar)}{rest}"; - } + public readonly int TitleId = getIndex(TitleIdCol); + public readonly int GameName = getIndex(GameNameCol); + public readonly int Labels = getIndex(LabelsCol); + public readonly int Status = getIndex(StatusCol); + public readonly int LastUpdated = getIndex(LastUpdatedCol); } } diff --git a/src/Ryujinx/Utilities/Configuration/AudioBackend.cs b/src/Ryujinx/Systems/Configuration/AudioBackend.cs similarity index 84% rename from src/Ryujinx/Utilities/Configuration/AudioBackend.cs rename to src/Ryujinx/Systems/Configuration/AudioBackend.cs index 8394bb282..a0aa30f38 100644 --- a/src/Ryujinx/Utilities/Configuration/AudioBackend.cs +++ b/src/Ryujinx/Systems/Configuration/AudioBackend.cs @@ -1,7 +1,7 @@ using Ryujinx.Common.Utilities; using System.Text.Json.Serialization; -namespace Ryujinx.Ava.Utilities.Configuration +namespace Ryujinx.Ava.Systems.Configuration { [JsonConverter(typeof(TypedStringEnumConverter))] public enum AudioBackend diff --git a/src/Ryujinx/Utilities/Configuration/ConfigurationFileFormat.cs b/src/Ryujinx/Systems/Configuration/ConfigurationFileFormat.cs similarity index 98% rename from src/Ryujinx/Utilities/Configuration/ConfigurationFileFormat.cs rename to src/Ryujinx/Systems/Configuration/ConfigurationFileFormat.cs index 822e95c24..ab841e51c 100644 --- a/src/Ryujinx/Utilities/Configuration/ConfigurationFileFormat.cs +++ b/src/Ryujinx/Systems/Configuration/ConfigurationFileFormat.cs @@ -1,5 +1,5 @@ -using Ryujinx.Ava.Utilities.Configuration.System; -using Ryujinx.Ava.Utilities.Configuration.UI; +using Ryujinx.Ava.Systems.Configuration.System; +using Ryujinx.Ava.Systems.Configuration.UI; using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration.Hid; using Ryujinx.Common.Configuration.Multiplayer; @@ -8,14 +8,14 @@ using Ryujinx.Common.Utilities; using Ryujinx.HLE; using System.Collections.Generic; -namespace Ryujinx.Ava.Utilities.Configuration +namespace Ryujinx.Ava.Systems.Configuration { public class ConfigurationFileFormat { /// /// The current version of the file format /// - public const int CurrentVersion = 67; + public const int CurrentVersion = 68; /// /// Version of the configuration file format diff --git a/src/Ryujinx/Utilities/Configuration/ConfigurationFileFormatSettings.cs b/src/Ryujinx/Systems/Configuration/ConfigurationFileFormatSettings.cs similarity index 83% rename from src/Ryujinx/Utilities/Configuration/ConfigurationFileFormatSettings.cs rename to src/Ryujinx/Systems/Configuration/ConfigurationFileFormatSettings.cs index 175d4dee8..1d0350dd3 100644 --- a/src/Ryujinx/Utilities/Configuration/ConfigurationFileFormatSettings.cs +++ b/src/Ryujinx/Systems/Configuration/ConfigurationFileFormatSettings.cs @@ -1,6 +1,6 @@ using Ryujinx.Common.Utilities; -namespace Ryujinx.Ava.Utilities.Configuration +namespace Ryujinx.Ava.Systems.Configuration { internal static class ConfigurationFileFormatSettings { diff --git a/src/Ryujinx/Utilities/Configuration/ConfigurationJsonSerializerContext.cs b/src/Ryujinx/Systems/Configuration/ConfigurationJsonSerializerContext.cs similarity index 84% rename from src/Ryujinx/Utilities/Configuration/ConfigurationJsonSerializerContext.cs rename to src/Ryujinx/Systems/Configuration/ConfigurationJsonSerializerContext.cs index a81e00f4a..8574b3a93 100644 --- a/src/Ryujinx/Utilities/Configuration/ConfigurationJsonSerializerContext.cs +++ b/src/Ryujinx/Systems/Configuration/ConfigurationJsonSerializerContext.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Ryujinx.Ava.Utilities.Configuration +namespace Ryujinx.Ava.Systems.Configuration { [JsonSourceGenerationOptions(WriteIndented = true)] [JsonSerializable(typeof(ConfigurationFileFormat))] diff --git a/src/Ryujinx/Utilities/Configuration/ConfigurationState.Migration.cs b/src/Ryujinx/Systems/Configuration/ConfigurationState.Migration.cs similarity index 75% rename from src/Ryujinx/Utilities/Configuration/ConfigurationState.Migration.cs rename to src/Ryujinx/Systems/Configuration/ConfigurationState.Migration.cs index 2d9ae864e..323fcb393 100644 --- a/src/Ryujinx/Utilities/Configuration/ConfigurationState.Migration.cs +++ b/src/Ryujinx/Systems/Configuration/ConfigurationState.Migration.cs @@ -1,7 +1,7 @@ using Avalonia.Media; using Gommon; -using Ryujinx.Ava.Utilities.Configuration.System; -using Ryujinx.Ava.Utilities.Configuration.UI; +using Ryujinx.Ava.Systems.Configuration.System; +using Ryujinx.Ava.Systems.Configuration.UI; using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration.Hid; using Ryujinx.Common.Configuration.Hid.Controller; @@ -14,13 +14,14 @@ using System.Collections.Generic; using System.Linq; using RyuLogger = Ryujinx.Common.Logging.Logger; -namespace Ryujinx.Ava.Utilities.Configuration +namespace Ryujinx.Ava.Systems.Configuration { public partial class ConfigurationState { - public void Load(ConfigurationFileFormat cff, string configurationFilePath) + public void Load(ConfigurationFileFormat cff, string configurationFilePath, string titleId = "") { bool configurationFileUpdated = false; + bool shouldLoadFromFile = string.IsNullOrEmpty(titleId); if (cff.Version is < 0 or > ConfigurationFileFormat.CurrentVersion) { @@ -43,16 +44,16 @@ namespace Ryujinx.Ava.Utilities.Configuration configurationFileUpdated = true; } + EnableDiscordIntegration.Value = cff.EnableDiscordIntegration; - CheckUpdatesOnStart.Value = cff.CheckUpdatesOnStart; - UpdateCheckerType.Value = cff.UpdateCheckerType; + UpdateCheckerType.Value = shouldLoadFromFile ? cff.UpdateCheckerType : UpdateCheckerType.Value; // Get from global config only FocusLostActionType.Value = cff.FocusLostActionType; - ShowConfirmExit.Value = cff.ShowConfirmExit; - RememberWindowState.Value = cff.RememberWindowState; - ShowTitleBar.Value = cff.ShowTitleBar; - EnableHardwareAcceleration.Value = cff.EnableHardwareAcceleration; + ShowConfirmExit.Value = shouldLoadFromFile ? cff.ShowConfirmExit : ShowConfirmExit.Value; // Get from global config only + RememberWindowState.Value = shouldLoadFromFile ? cff.RememberWindowState : RememberWindowState.Value; // Get from global config only + ShowOldUI.Value = shouldLoadFromFile ? cff.ShowTitleBar : ShowOldUI.Value; // Get from global config only + EnableHardwareAcceleration.Value = shouldLoadFromFile ? cff.EnableHardwareAcceleration : EnableHardwareAcceleration.Value; // Get from global config only HideCursor.Value = cff.HideCursor; - + Logger.EnableFileLog.Value = cff.EnableFileLog; Logger.EnableDebug.Value = cff.LoggingEnableDebug; Logger.EnableStub.Value = cff.LoggingEnableStub; @@ -87,8 +88,8 @@ namespace Ryujinx.Ava.Utilities.Configuration System.Language.Value = cff.SystemLanguage; System.Region.Value = cff.SystemRegion; System.TimeZone.Value = cff.SystemTimeZone; - System.SystemTimeOffset.Value = cff.SystemTimeOffset; - System.MatchSystemTime.Value = cff.MatchSystemTime; + System.SystemTimeOffset.Value = shouldLoadFromFile ? cff.SystemTimeOffset : System.SystemTimeOffset.Value; // Get from global config only + System.MatchSystemTime.Value = shouldLoadFromFile ? cff.MatchSystemTime : System.MatchSystemTime.Value; // Get from global config only System.EnableDockedMode.Value = cff.DockedMode; System.EnablePtc.Value = cff.EnablePtc; System.EnableLowPowerPtc.Value = cff.EnableLowPowerPtc; @@ -103,53 +104,54 @@ namespace Ryujinx.Ava.Utilities.Configuration System.IgnoreControllerApplet.Value = cff.IgnoreApplet; System.UseHypervisor.Value = cff.UseHypervisor; - System.MissingAppletsAsReal.Value = cff.MissingAppletsAsReal; - System.SoftwareKeyboardIsReal.Value = cff.SoftwareKeyboardIsReal; - System.BrowserIsReal.Value = cff.BrowserIsReal; - System.ControllerIsReal.Value = cff.ControllerIsReal; - System.PlayerSelectIsReal.Value = cff.PlayerSelectIsReal; + System.MissingAppletsAsReal.Value = cff.MissingAppletsAsReal; + System.SoftwareKeyboardIsReal.Value = cff.SoftwareKeyboardIsReal; + System.BrowserIsReal.Value = cff.BrowserIsReal; + System.ControllerIsReal.Value = cff.ControllerIsReal; + System.PlayerSelectIsReal.Value = cff.PlayerSelectIsReal; System.CabinetIsReal.Value = cff.CabinetIsReal; - - UI.GuiColumns.FavColumn.Value = cff.GuiColumns.FavColumn; - UI.GuiColumns.IconColumn.Value = cff.GuiColumns.IconColumn; - UI.GuiColumns.AppColumn.Value = cff.GuiColumns.AppColumn; - UI.GuiColumns.DevColumn.Value = cff.GuiColumns.DevColumn; - UI.GuiColumns.VersionColumn.Value = cff.GuiColumns.VersionColumn; - UI.GuiColumns.TimePlayedColumn.Value = cff.GuiColumns.TimePlayedColumn; - UI.GuiColumns.LastPlayedColumn.Value = cff.GuiColumns.LastPlayedColumn; - UI.GuiColumns.FileExtColumn.Value = cff.GuiColumns.FileExtColumn; - UI.GuiColumns.FileSizeColumn.Value = cff.GuiColumns.FileSizeColumn; - UI.GuiColumns.PathColumn.Value = cff.GuiColumns.PathColumn; - UI.ColumnSort.SortColumnId.Value = cff.ColumnSort.SortColumnId; - UI.ColumnSort.SortAscending.Value = cff.ColumnSort.SortAscending; - UI.GameDirs.Value = cff.GameDirs; - UI.AutoloadDirs.Value = cff.AutoloadDirs ?? []; - UI.ShownFileTypes.NSP.Value = cff.ShownFileTypes.NSP; - UI.ShownFileTypes.PFS0.Value = cff.ShownFileTypes.PFS0; - UI.ShownFileTypes.XCI.Value = cff.ShownFileTypes.XCI; - UI.ShownFileTypes.NCA.Value = cff.ShownFileTypes.NCA; - UI.ShownFileTypes.NRO.Value = cff.ShownFileTypes.NRO; - UI.ShownFileTypes.NSO.Value = cff.ShownFileTypes.NSO; - UI.LanguageCode.Value = cff.LanguageCode; - UI.BaseStyle.Value = cff.BaseStyle; - UI.GameListViewMode.Value = cff.GameListViewMode; - UI.ShowNames.Value = cff.ShowNames; - UI.IsAscendingOrder.Value = cff.IsAscendingOrder; - UI.GridSize.Value = cff.GridSize; - UI.ApplicationSort.Value = cff.ApplicationSort; - UI.StartFullscreen.Value = cff.StartFullscreen; - UI.StartNoUI.Value = cff.StartNoUI; - UI.ShowConsole.Value = cff.ShowConsole; - UI.WindowStartup.WindowSizeWidth.Value = cff.WindowStartup.WindowSizeWidth; - UI.WindowStartup.WindowSizeHeight.Value = cff.WindowStartup.WindowSizeHeight; - UI.WindowStartup.WindowPositionX.Value = cff.WindowStartup.WindowPositionX; - UI.WindowStartup.WindowPositionY.Value = cff.WindowStartup.WindowPositionY; - UI.WindowStartup.WindowMaximized.Value = cff.WindowStartup.WindowMaximized; - + + UI.GuiColumns.FavColumn.Value = shouldLoadFromFile ? cff.GuiColumns.FavColumn : UI.GuiColumns.FavColumn.Value; + UI.GuiColumns.IconColumn.Value = shouldLoadFromFile ? cff.GuiColumns.IconColumn : UI.GuiColumns.IconColumn.Value; + UI.GuiColumns.AppColumn.Value = shouldLoadFromFile ? cff.GuiColumns.AppColumn : UI.GuiColumns.AppColumn.Value; + UI.GuiColumns.DevColumn.Value = shouldLoadFromFile ? cff.GuiColumns.DevColumn : UI.GuiColumns.DevColumn.Value; + UI.GuiColumns.VersionColumn.Value = shouldLoadFromFile ? cff.GuiColumns.VersionColumn : UI.GuiColumns.VersionColumn.Value; + UI.GuiColumns.TimePlayedColumn.Value = shouldLoadFromFile ? cff.GuiColumns.TimePlayedColumn : UI.GuiColumns.TimePlayedColumn.Value; + UI.GuiColumns.LastPlayedColumn.Value = shouldLoadFromFile ? cff.GuiColumns.LastPlayedColumn : UI.GuiColumns.LastPlayedColumn.Value; + UI.GuiColumns.FileExtColumn.Value = shouldLoadFromFile ? cff.GuiColumns.FileExtColumn : UI.GuiColumns.FileExtColumn.Value; + UI.GuiColumns.FileSizeColumn.Value = shouldLoadFromFile ? cff.GuiColumns.FileSizeColumn : UI.GuiColumns.FileSizeColumn.Value; + UI.GuiColumns.PathColumn.Value = shouldLoadFromFile ? cff.GuiColumns.PathColumn : UI.GuiColumns.PathColumn.Value; + UI.ColumnSort.SortColumnId.Value = shouldLoadFromFile ? cff.ColumnSort.SortColumnId : UI.ColumnSort.SortColumnId.Value; + UI.ColumnSort.SortAscending.Value = shouldLoadFromFile ? cff.ColumnSort.SortAscending : UI.ColumnSort.SortAscending.Value; + UI.GameDirs.Value = shouldLoadFromFile ? cff.GameDirs : UI.GameDirs.Value; + UI.AutoloadDirs.Value = shouldLoadFromFile ? (cff.AutoloadDirs ?? []) : UI.AutoloadDirs.Value; + UI.ShownFileTypes.NSP.Value = shouldLoadFromFile ? cff.ShownFileTypes.NSP : UI.ShownFileTypes.NSP.Value; + UI.ShownFileTypes.PFS0.Value = shouldLoadFromFile ? cff.ShownFileTypes.PFS0 : UI.ShownFileTypes.PFS0.Value; + UI.ShownFileTypes.XCI.Value = shouldLoadFromFile ? cff.ShownFileTypes.XCI : UI.ShownFileTypes.XCI.Value; + UI.ShownFileTypes.NCA.Value = shouldLoadFromFile ? cff.ShownFileTypes.NCA : UI.ShownFileTypes.NCA.Value; + UI.ShownFileTypes.NRO.Value = shouldLoadFromFile ? cff.ShownFileTypes.NRO : UI.ShownFileTypes.NRO.Value; + UI.ShownFileTypes.NSO.Value = shouldLoadFromFile ? cff.ShownFileTypes.NSO : UI.ShownFileTypes.NSO.Value; + UI.LanguageCode.Value = shouldLoadFromFile ? cff.LanguageCode : UI.LanguageCode.Value; + UI.BaseStyle.Value = shouldLoadFromFile ? cff.BaseStyle : UI.BaseStyle.Value; + UI.GameListViewMode.Value = shouldLoadFromFile ? cff.GameListViewMode : UI.GameListViewMode.Value; + UI.ShowNames.Value = shouldLoadFromFile ? cff.ShowNames : UI.ShowNames.Value; + UI.IsAscendingOrder.Value = shouldLoadFromFile ? cff.IsAscendingOrder : UI.IsAscendingOrder.Value; + UI.GridSize.Value = shouldLoadFromFile ? cff.GridSize : UI.GridSize.Value; + UI.ApplicationSort.Value = shouldLoadFromFile ? cff.ApplicationSort : UI.ApplicationSort.Value; + UI.StartFullscreen.Value = shouldLoadFromFile ? cff.StartFullscreen : UI.StartFullscreen.Value; + UI.StartNoUI.Value = shouldLoadFromFile ? cff.StartNoUI : UI.StartNoUI.Value; + UI.ShowConsole.Value = shouldLoadFromFile ? cff.ShowConsole : UI.ShowConsole.Value; + UI.WindowStartup.WindowSizeWidth.Value = shouldLoadFromFile ? cff.WindowStartup.WindowSizeWidth : UI.WindowStartup.WindowSizeWidth.Value; + UI.WindowStartup.WindowSizeHeight.Value = shouldLoadFromFile ? cff.WindowStartup.WindowSizeHeight : UI.WindowStartup.WindowSizeHeight.Value; + UI.WindowStartup.WindowPositionX.Value = shouldLoadFromFile ? cff.WindowStartup.WindowPositionX : UI.WindowStartup.WindowPositionX.Value; + UI.WindowStartup.WindowPositionY.Value = shouldLoadFromFile ? cff.WindowStartup.WindowPositionY : UI.WindowStartup.WindowPositionY.Value; + UI.WindowStartup.WindowMaximized.Value = shouldLoadFromFile ? cff.WindowStartup.WindowMaximized : UI.WindowStartup.WindowMaximized.Value; + + Hid.EnableKeyboard.Value = cff.EnableKeyboard; Hid.EnableMouse.Value = cff.EnableMouse; - Hid.DisableInputWhenOutOfFocus.Value = cff.DisableInputWhenOutOfFocus; - Hid.Hotkeys.Value = cff.Hotkeys; + Hid.DisableInputWhenOutOfFocus.Value = shouldLoadFromFile ? cff.DisableInputWhenOutOfFocus: Hid.DisableInputWhenOutOfFocus.Value; // Get from global config only + Hid.Hotkeys.Value = shouldLoadFromFile ? cff.Hotkeys : Hid.Hotkeys.Value; // Get from global config only Hid.InputConfig.Value = cff.InputConfig ?? []; Hid.RainbowSpeed.Value = cff.RainbowSpeed; @@ -165,9 +167,7 @@ namespace Ryujinx.Ava.Utilities.Configuration DirtyHacks hacks = new (cff.DirtyHacks ?? []); Hacks.Xc2MenuSoftlockFix.Value = hacks.IsEnabled(DirtyHack.Xc2MenuSoftlockFix); - - Hacks.EnableShaderTranslationDelay.Value = hacks.IsEnabled(DirtyHack.ShaderTranslationDelay); - Hacks.ShaderTranslationDelay.Value = hacks[DirtyHack.ShaderTranslationDelay].CoerceAtLeast(0); + } if (configurationFileUpdated) @@ -234,7 +234,6 @@ namespace Ryujinx.Ava.Utilities.Configuration ButtonZl = Key.Q, ButtonSl = Key.Unbound, ButtonSr = Key.Unbound, - ButtonCapture = Key.Unbound, }, LeftJoyconStick = new JoyconConfigKeyboardStick { @@ -255,7 +254,6 @@ namespace Ryujinx.Ava.Utilities.Configuration ButtonZr = Key.O, ButtonSl = Key.Unbound, ButtonSr = Key.Unbound, - ButtonHome = Key.Unbound, }, RightJoyconStick = new JoyconConfigKeyboardStick { @@ -427,7 +425,6 @@ namespace Ryujinx.Ava.Utilities.Configuration // This was accidentally enabled by default when it was PRed. That is not what we want, // so as a compromise users who want to use it will simply need to re-enable it once after updating. cff.IgnoreApplet = false; - cff.MissingAppletsAsReal = false; }), (60, static cff => cff.StartNoUI = false), (61, static cff => @@ -449,6 +446,8 @@ namespace Ryujinx.Ava.Utilities.Configuration (65, static cff => cff.UpdateCheckerType = cff.CheckUpdatesOnStart ? UpdaterType.PromptAtStartup : UpdaterType.Off), (66, static cff => cff.DisableInputWhenOutOfFocus = false), (67, static cff => cff.FocusLostActionType = cff.DisableInputWhenOutOfFocus ? FocusLostType.BlockInput : FocusLostType.DoNothing) + // 68 was the version that added per-game configs; the file structure did not change + // the version was increased so external tools could know that your Ryujinx version has per-game config capabilities. ); } } diff --git a/src/Ryujinx/Utilities/Configuration/ConfigurationState.Model.cs b/src/Ryujinx/Systems/Configuration/ConfigurationState.Model.cs similarity index 93% rename from src/Ryujinx/Utilities/Configuration/ConfigurationState.Model.cs rename to src/Ryujinx/Systems/Configuration/ConfigurationState.Model.cs index 3b7e8d47e..51a5796ca 100644 --- a/src/Ryujinx/Utilities/Configuration/ConfigurationState.Model.cs +++ b/src/Ryujinx/Systems/Configuration/ConfigurationState.Model.cs @@ -1,7 +1,8 @@ using ARMeilleure; using Gommon; -using Ryujinx.Ava.Utilities.Configuration.System; -using Ryujinx.Ava.Utilities.Configuration.UI; +using LibHac.Tools.FsSystem; +using Ryujinx.Ava.Systems.Configuration.System; +using Ryujinx.Ava.Systems.Configuration.UI; using Ryujinx.Common; using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration.Hid; @@ -14,11 +15,11 @@ using System.Collections.Generic; using System.Linq; using RyuLogger = Ryujinx.Common.Logging.Logger; -namespace Ryujinx.Ava.Utilities.Configuration +namespace Ryujinx.Ava.Systems.Configuration { public partial class ConfigurationState { - /// + /// /// UI configuration section /// public class UISection @@ -350,6 +351,10 @@ namespace Ryujinx.Ava.Utilities.Configuration /// public ReactiveObject EnableFsIntegrityChecks { get; private set; } + public IntegrityCheckLevel IntegrityCheckLevel => EnableFsIntegrityChecks + ? IntegrityCheckLevel.ErrorOnInvalid + : IntegrityCheckLevel.None; + /// /// Enables FS access log output to the console. Possible modes are 0-3 /// @@ -690,6 +695,14 @@ namespace Ryujinx.Ava.Utilities.Configuration /// public ReactiveObject LdnServer { get; private set; } + public string GetLdnServer() + { + string ldnServer = LdnServer; + return string.IsNullOrEmpty(ldnServer) + ? SharedConstants.DefaultLanPlayHost + : ldnServer; + } + public MultiplayerSection() { LanInterfaceId = new ReactiveObject(); @@ -713,18 +726,15 @@ namespace Ryujinx.Ava.Utilities.Configuration public ReactiveObject Xc2MenuSoftlockFix { get; private set; } - public ReactiveObject EnableShaderTranslationDelay { get; private set; } - - public ReactiveObject ShaderTranslationDelay { get; private set; } + public ReactiveObject DisableNifmIsAnyInternetRequestAccepted { get; private set; } public HacksSection() { ShowDirtyHacks = new ReactiveObject(); Xc2MenuSoftlockFix = new ReactiveObject(); Xc2MenuSoftlockFix.Event += HackChanged; - EnableShaderTranslationDelay = new ReactiveObject(); - EnableShaderTranslationDelay.Event += HackChanged; - ShaderTranslationDelay = new ReactiveObject(); + DisableNifmIsAnyInternetRequestAccepted = new ReactiveObject(); + DisableNifmIsAnyInternetRequestAccepted.Event += HackChanged; } private void HackChanged(object sender, ReactiveEventArgs rxe) @@ -755,8 +765,8 @@ namespace Ryujinx.Ava.Utilities.Configuration if (Xc2MenuSoftlockFix) Apply(DirtyHack.Xc2MenuSoftlockFix); - if (EnableShaderTranslationDelay) - Apply(DirtyHack.ShaderTranslationDelay, ShaderTranslationDelay); + if (DisableNifmIsAnyInternetRequestAccepted) + Apply(DirtyHack.NifmServiceDisableIsAnyInternetRequestAccepted); return enabledHacks.ToArray(); @@ -812,11 +822,6 @@ namespace Ryujinx.Ava.Utilities.Configuration /// Enables or disables Discord Rich Presence /// public ReactiveObject EnableDiscordIntegration { get; private set; } - - /// - /// Checks for updates when Ryujinx starts when enabled - /// - public ReactiveObject CheckUpdatesOnStart { get; private set; } /// /// Checks for updates when Ryujinx starts when enabled, either prompting when an update is found or just showing a notification. @@ -839,9 +844,9 @@ namespace Ryujinx.Ava.Utilities.Configuration public ReactiveObject RememberWindowState { get; private set; } /// - /// Enables or disables the redesigned title bar + /// Disable the new title bar layout & window layout changes. /// - public ReactiveObject ShowTitleBar { get; private set; } + public ReactiveObject ShowOldUI { get; private set; } /// /// Enables hardware-accelerated rendering for Avalonia @@ -862,15 +867,44 @@ namespace Ryujinx.Ava.Utilities.Configuration Hid = new HidSection(); Multiplayer = new MultiplayerSection(); Hacks = new HacksSection(); - EnableDiscordIntegration = new ReactiveObject(); - CheckUpdatesOnStart = new ReactiveObject(); UpdateCheckerType = new ReactiveObject(); FocusLostActionType = new ReactiveObject(); + HideCursor = new ReactiveObject(); + EnableDiscordIntegration = new ReactiveObject(); ShowConfirmExit = new ReactiveObject(); RememberWindowState = new ReactiveObject(); - ShowTitleBar = new ReactiveObject(); + ShowOldUI = new ReactiveObject(); EnableHardwareAcceleration = new ReactiveObject(); - HideCursor = new ReactiveObject(); } + + public HleConfiguration CreateHleConfiguration() => + new( + System.DramSize, + System.Language.Value.ToHLE(), + System.Region.Value.ToHLE(), + Graphics.VSyncMode, + System.EnableDockedMode, + System.EnablePtc, + System.EnableInternetAccess, + System.EnableFsIntegrityChecks + ? IntegrityCheckLevel.ErrorOnInvalid + : IntegrityCheckLevel.None, + System.FsGlobalAccessLogMode, + System.MatchSystemTime + ? 0 + : System.SystemTimeOffset, + System.TimeZone, + System.MemoryManagerMode, + System.IgnoreMissingServices, + Graphics.AspectRatio, + System.AudioVolume, + System.UseHypervisor, + Multiplayer.LanInterfaceId, + Multiplayer.Mode, + Multiplayer.DisableP2p, + Multiplayer.LdnPassphrase, + Instance.Multiplayer.GetLdnServer(), + Instance.Graphics.CustomVSyncInterval, + Instance.Hacks.ShowDirtyHacks ? Instance.Hacks.EnabledHacks : null); } } diff --git a/src/Ryujinx/Utilities/Configuration/ConfigurationState.cs b/src/Ryujinx/Systems/Configuration/ConfigurationState.cs similarity index 95% rename from src/Ryujinx/Utilities/Configuration/ConfigurationState.cs rename to src/Ryujinx/Systems/Configuration/ConfigurationState.cs index 9fc704838..4a51e710a 100644 --- a/src/Ryujinx/Utilities/Configuration/ConfigurationState.cs +++ b/src/Ryujinx/Systems/Configuration/ConfigurationState.cs @@ -1,5 +1,5 @@ -using Ryujinx.Ava.Utilities.Configuration.System; -using Ryujinx.Ava.Utilities.Configuration.UI; +using Ryujinx.Ava.Systems.Configuration.System; +using Ryujinx.Ava.Systems.Configuration.UI; using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration.Hid; using Ryujinx.Common.Configuration.Hid.Keyboard; @@ -9,7 +9,7 @@ using Ryujinx.HLE; using System; using System.Linq; -namespace Ryujinx.Ava.Utilities.Configuration +namespace Ryujinx.Ava.Systems.Configuration { public partial class ConfigurationState { @@ -56,12 +56,11 @@ namespace Ryujinx.Ava.Utilities.Configuration MatchSystemTime = System.MatchSystemTime, DockedMode = System.EnableDockedMode, EnableDiscordIntegration = EnableDiscordIntegration, - CheckUpdatesOnStart = CheckUpdatesOnStart, UpdateCheckerType = UpdateCheckerType, FocusLostActionType = FocusLostActionType, ShowConfirmExit = ShowConfirmExit, RememberWindowState = RememberWindowState, - ShowTitleBar = ShowTitleBar, + ShowTitleBar = ShowOldUI, EnableHardwareAcceleration = EnableHardwareAcceleration, HideCursor = HideCursor, VSyncMode = Graphics.VSyncMode, @@ -189,7 +188,7 @@ namespace Ryujinx.Ava.Utilities.Configuration FocusLostActionType.Value = FocusLostType.DoNothing; ShowConfirmExit.Value = true; RememberWindowState.Value = true; - ShowTitleBar.Value = !OperatingSystem.IsWindows(); + ShowOldUI.Value = !OperatingSystem.IsWindows(); EnableHardwareAcceleration.Value = true; HideCursor.Value = HideCursorMode.OnIdle; Graphics.VSyncMode.Value = VSyncMode.Switch; @@ -332,15 +331,14 @@ namespace Ryujinx.Ava.Utilities.Configuration private static GraphicsBackend DefaultGraphicsBackend() { - // Any system running macOS should default to auto, so it uses Vulkan everywhere and Metal in games where it works well. - if (OperatingSystem.IsMacOS()) - return GraphicsBackend.Auto; - - // Any system returning any amount of valid Vulkan devices should default to Vulkan. + // Any system running macOS or returning any amount of valid Vulkan devices should default to Vulkan. // Checks for if the Vulkan version and featureset is compatible should be performed within VulkanRenderer. - return VulkanRenderer.GetPhysicalDevices().Length > 0 - ? GraphicsBackend.Vulkan - : GraphicsBackend.OpenGl; + if (OperatingSystem.IsMacOS() || VulkanRenderer.GetPhysicalDevices().Length > 0) + { + return GraphicsBackend.Vulkan; + } + + return GraphicsBackend.OpenGl; } } } diff --git a/src/Ryujinx/Utilities/Configuration/FileTypes.cs b/src/Ryujinx/Systems/Configuration/FileTypes.cs similarity index 90% rename from src/Ryujinx/Utilities/Configuration/FileTypes.cs rename to src/Ryujinx/Systems/Configuration/FileTypes.cs index c4550b5a6..70517683b 100644 --- a/src/Ryujinx/Utilities/Configuration/FileTypes.cs +++ b/src/Ryujinx/Systems/Configuration/FileTypes.cs @@ -1,8 +1,8 @@ using System; -using static Ryujinx.Ava.Utilities.Configuration.ConfigurationState.UISection; +using static Ryujinx.Ava.Systems.Configuration.ConfigurationState.UISection; -namespace Ryujinx.Ava.Utilities.Configuration +namespace Ryujinx.Ava.Systems.Configuration { public enum FileTypes { diff --git a/src/Ryujinx/Utilities/Configuration/LoggerModule.cs b/src/Ryujinx/Systems/Configuration/LoggerModule.cs similarity index 98% rename from src/Ryujinx/Utilities/Configuration/LoggerModule.cs rename to src/Ryujinx/Systems/Configuration/LoggerModule.cs index f6c1be082..941556c43 100644 --- a/src/Ryujinx/Utilities/Configuration/LoggerModule.cs +++ b/src/Ryujinx/Systems/Configuration/LoggerModule.cs @@ -4,7 +4,7 @@ using Ryujinx.Common.Logging.Targets; using System; using System.IO; -namespace Ryujinx.Ava.Utilities.Configuration +namespace Ryujinx.Ava.Systems.Configuration { public static class LoggerModule { diff --git a/src/Ryujinx/Utilities/Configuration/System/Language.cs b/src/Ryujinx/Systems/Configuration/System/Language.cs similarity index 58% rename from src/Ryujinx/Utilities/Configuration/System/Language.cs rename to src/Ryujinx/Systems/Configuration/System/Language.cs index 81a9bd192..ff1476b73 100644 --- a/src/Ryujinx/Utilities/Configuration/System/Language.cs +++ b/src/Ryujinx/Systems/Configuration/System/Language.cs @@ -1,7 +1,7 @@ using Ryujinx.Common.Utilities; using System.Text.Json.Serialization; -namespace Ryujinx.Ava.Utilities.Configuration.System +namespace Ryujinx.Ava.Systems.Configuration.System { [JsonConverter(typeof(TypedStringEnumConverter))] public enum Language @@ -25,4 +25,13 @@ namespace Ryujinx.Ava.Utilities.Configuration.System TraditionalChinese, BrazilianPortuguese, } + + public static class LanguageEnumHelper + { + public static Language ToUI(this HLE.HOS.SystemState.SystemLanguage hleLanguage) + => (Language)hleLanguage; + + public static HLE.HOS.SystemState.SystemLanguage ToHLE(this Language uiLanguage) + => (HLE.HOS.SystemState.SystemLanguage)uiLanguage; + } } diff --git a/src/Ryujinx/Systems/Configuration/System/Region.cs b/src/Ryujinx/Systems/Configuration/System/Region.cs new file mode 100644 index 000000000..0089f073c --- /dev/null +++ b/src/Ryujinx/Systems/Configuration/System/Region.cs @@ -0,0 +1,26 @@ +using Ryujinx.Common.Utilities; +using System.Text.Json.Serialization; + +namespace Ryujinx.Ava.Systems.Configuration.System +{ + [JsonConverter(typeof(TypedStringEnumConverter))] + public enum Region + { + Japan, + USA, + Europe, + Australia, + China, + Korea, + Taiwan, + } + + public static class RegionEnumHelper + { + public static Region ToUI(this HLE.HOS.SystemState.RegionCode hleRegion) + => (Region)hleRegion; + + public static HLE.HOS.SystemState.RegionCode ToHLE(this Region uiRegion) + => (HLE.HOS.SystemState.RegionCode)uiRegion; + } +} diff --git a/src/Ryujinx/Utilities/Configuration/UI/ColumnSort.cs b/src/Ryujinx/Systems/Configuration/UI/ColumnSort.cs similarity index 73% rename from src/Ryujinx/Utilities/Configuration/UI/ColumnSort.cs rename to src/Ryujinx/Systems/Configuration/UI/ColumnSort.cs index e74ca0ec5..e5f54c799 100644 --- a/src/Ryujinx/Utilities/Configuration/UI/ColumnSort.cs +++ b/src/Ryujinx/Systems/Configuration/UI/ColumnSort.cs @@ -1,4 +1,4 @@ -namespace Ryujinx.Ava.Utilities.Configuration.UI +namespace Ryujinx.Ava.Systems.Configuration.UI { public struct ColumnSort { diff --git a/src/Ryujinx/Utilities/Configuration/UI/FocusLostType.cs b/src/Ryujinx/Systems/Configuration/UI/FocusLostType.cs similarity index 86% rename from src/Ryujinx/Utilities/Configuration/UI/FocusLostType.cs rename to src/Ryujinx/Systems/Configuration/UI/FocusLostType.cs index eea588539..2d0341022 100644 --- a/src/Ryujinx/Utilities/Configuration/UI/FocusLostType.cs +++ b/src/Ryujinx/Systems/Configuration/UI/FocusLostType.cs @@ -1,7 +1,7 @@ using Ryujinx.Common.Utilities; using System.Text.Json.Serialization; -namespace Ryujinx.Ava.Utilities.Configuration.UI +namespace Ryujinx.Ava.Systems.Configuration.UI { [JsonConverter(typeof(TypedStringEnumConverter))] public enum FocusLostType diff --git a/src/Ryujinx/Utilities/Configuration/UI/GuiColumns.cs b/src/Ryujinx/Systems/Configuration/UI/GuiColumns.cs similarity index 91% rename from src/Ryujinx/Utilities/Configuration/UI/GuiColumns.cs rename to src/Ryujinx/Systems/Configuration/UI/GuiColumns.cs index 0ab9885fe..a0021be96 100644 --- a/src/Ryujinx/Utilities/Configuration/UI/GuiColumns.cs +++ b/src/Ryujinx/Systems/Configuration/UI/GuiColumns.cs @@ -1,4 +1,4 @@ -namespace Ryujinx.Ava.Utilities.Configuration.UI +namespace Ryujinx.Ava.Systems.Configuration.UI { public struct GuiColumns { diff --git a/src/Ryujinx/Utilities/Configuration/UI/ShownFileTypes.cs b/src/Ryujinx/Systems/Configuration/UI/ShownFileTypes.cs similarity index 85% rename from src/Ryujinx/Utilities/Configuration/UI/ShownFileTypes.cs rename to src/Ryujinx/Systems/Configuration/UI/ShownFileTypes.cs index 9541b4885..bc32bd153 100644 --- a/src/Ryujinx/Utilities/Configuration/UI/ShownFileTypes.cs +++ b/src/Ryujinx/Systems/Configuration/UI/ShownFileTypes.cs @@ -1,4 +1,4 @@ -namespace Ryujinx.Ava.Utilities.Configuration.UI +namespace Ryujinx.Ava.Systems.Configuration.UI { public struct ShownFileTypes { diff --git a/src/Ryujinx/Utilities/Configuration/UI/UpdaterType.cs b/src/Ryujinx/Systems/Configuration/UI/UpdaterType.cs similarity index 83% rename from src/Ryujinx/Utilities/Configuration/UI/UpdaterType.cs rename to src/Ryujinx/Systems/Configuration/UI/UpdaterType.cs index 2ab17a497..06cf35d10 100644 --- a/src/Ryujinx/Utilities/Configuration/UI/UpdaterType.cs +++ b/src/Ryujinx/Systems/Configuration/UI/UpdaterType.cs @@ -1,7 +1,7 @@ using Ryujinx.Common.Utilities; using System.Text.Json.Serialization; -namespace Ryujinx.Ava.Utilities.Configuration.UI +namespace Ryujinx.Ava.Systems.Configuration.UI { [JsonConverter(typeof(TypedStringEnumConverter))] public enum UpdaterType diff --git a/src/Ryujinx/Utilities/Configuration/UI/WindowStartup.cs b/src/Ryujinx/Systems/Configuration/UI/WindowStartup.cs similarity index 85% rename from src/Ryujinx/Utilities/Configuration/UI/WindowStartup.cs rename to src/Ryujinx/Systems/Configuration/UI/WindowStartup.cs index 6c5e36879..be93ef4b6 100644 --- a/src/Ryujinx/Utilities/Configuration/UI/WindowStartup.cs +++ b/src/Ryujinx/Systems/Configuration/UI/WindowStartup.cs @@ -1,4 +1,4 @@ -namespace Ryujinx.Ava.Utilities.Configuration.UI +namespace Ryujinx.Ava.Systems.Configuration.UI { public struct WindowStartup { diff --git a/src/Ryujinx/DiscordIntegrationModule.cs b/src/Ryujinx/Systems/DiscordIntegrationModule.cs similarity index 96% rename from src/Ryujinx/DiscordIntegrationModule.cs rename to src/Ryujinx/Systems/DiscordIntegrationModule.cs index 8d232b4fb..8a3b42ac8 100644 --- a/src/Ryujinx/DiscordIntegrationModule.cs +++ b/src/Ryujinx/Systems/DiscordIntegrationModule.cs @@ -1,18 +1,17 @@ using DiscordRPC; using Gommon; using Ryujinx.Ava.Utilities; -using Ryujinx.Ava.Utilities.AppLibrary; -using Ryujinx.Ava.Utilities.Configuration; -using Ryujinx.Ava.Utilities.PlayReport; +using Ryujinx.Ava.Systems.AppLibrary; +using Ryujinx.Ava.Systems.Configuration; +using Ryujinx.Ava.Systems.PlayReport; using Ryujinx.Common; using Ryujinx.Common.Logging; using Ryujinx.HLE; using Ryujinx.HLE.Loaders.Processes; using Ryujinx.Horizon; -using Ryujinx.Horizon.Prepo.Types; using System.Text; -namespace Ryujinx.Ava +namespace Ryujinx.Ava.Systems { public static class DiscordIntegrationModule { @@ -124,7 +123,7 @@ namespace Ryujinx.Ava _currentApp = null; } - private static void HandlePlayReport(PlayReport playReport) + private static void HandlePlayReport(Horizon.Prepo.Types.PlayReport playReport) { if (_discordClient is null) return; if (!TitleIDs.CurrentApplication.Value.HasValue) return; diff --git a/src/Ryujinx/Utilities/PlayReport/Analyzer.cs b/src/Ryujinx/Systems/PlayReport/Analyzer.cs similarity index 98% rename from src/Ryujinx/Utilities/PlayReport/Analyzer.cs rename to src/Ryujinx/Systems/PlayReport/Analyzer.cs index 8faf4fb31..d4198cdcf 100644 --- a/src/Ryujinx/Utilities/PlayReport/Analyzer.cs +++ b/src/Ryujinx/Systems/PlayReport/Analyzer.cs @@ -1,5 +1,5 @@ using Gommon; -using Ryujinx.Ava.Utilities.AppLibrary; +using Ryujinx.Ava.Systems.AppLibrary; using Ryujinx.Common.Logging; using System; using System.Collections.Generic; @@ -7,7 +7,7 @@ using System.Collections.ObjectModel; using System.Globalization; using System.Linq; -namespace Ryujinx.Ava.Utilities.PlayReport +namespace Ryujinx.Ava.Systems.PlayReport { /// /// The entrypoint for the Play Report analysis system. diff --git a/src/Ryujinx/Utilities/PlayReport/Delegates.cs b/src/Ryujinx/Systems/PlayReport/Delegates.cs similarity index 97% rename from src/Ryujinx/Utilities/PlayReport/Delegates.cs rename to src/Ryujinx/Systems/PlayReport/Delegates.cs index 92569d32e..b3496ec4d 100644 --- a/src/Ryujinx/Utilities/PlayReport/Delegates.cs +++ b/src/Ryujinx/Systems/PlayReport/Delegates.cs @@ -1,4 +1,4 @@ -namespace Ryujinx.Ava.Utilities.PlayReport +namespace Ryujinx.Ava.Systems.PlayReport { /// /// The delegate type that powers single value formatters.
diff --git a/src/Ryujinx/Utilities/PlayReport/MatchedValues.cs b/src/Ryujinx/Systems/PlayReport/MatchedValues.cs similarity index 96% rename from src/Ryujinx/Utilities/PlayReport/MatchedValues.cs rename to src/Ryujinx/Systems/PlayReport/MatchedValues.cs index 46cae678a..025130758 100644 --- a/src/Ryujinx/Utilities/PlayReport/MatchedValues.cs +++ b/src/Ryujinx/Systems/PlayReport/MatchedValues.cs @@ -1,8 +1,8 @@ using MsgPack; -using Ryujinx.Ava.Utilities.AppLibrary; +using Ryujinx.Ava.Systems.AppLibrary; using System.Collections.Generic; -namespace Ryujinx.Ava.Utilities.PlayReport +namespace Ryujinx.Ava.Systems.PlayReport { public abstract class MatchedValue { diff --git a/src/Ryujinx/Utilities/PlayReport/PlayReports.Formatters.cs b/src/Ryujinx/Systems/PlayReport/PlayReports.Formatters.cs similarity index 98% rename from src/Ryujinx/Utilities/PlayReport/PlayReports.Formatters.cs rename to src/Ryujinx/Systems/PlayReport/PlayReports.Formatters.cs index 43e830819..5c5fb40f4 100644 --- a/src/Ryujinx/Utilities/PlayReport/PlayReports.Formatters.cs +++ b/src/Ryujinx/Systems/PlayReport/PlayReports.Formatters.cs @@ -5,7 +5,7 @@ using System.Buffers.Binary; using System.Collections.Generic; using System.Linq; -namespace Ryujinx.Ava.Utilities.PlayReport +namespace Ryujinx.Ava.Systems.PlayReport { public partial class PlayReports { @@ -59,12 +59,13 @@ namespace Ryujinx.Ava.Utilities.PlayReport "Race" => "Racing", _ => FormattedValue.ForceReset }; - - private static FormattedValue PokemonSVUnionCircle(SingleValue value) - => value.Matched.BoxedValue is 0 ? "Playing Alone" : "Playing in a group"; - - private static FormattedValue PokemonSVArea(SingleValue value) - => value.Matched.StringValue switch + + private static FormattedValue PokemonSV(MultiValue values) + { + + string playStatus = values.Matched[0].BoxedValue is 0 ? "Playing Alone" : "Playing in a group"; + + FormattedValue locations = values.Matched[1].ToString() switch { // Base Game Locations "a_w01" => "South Area One", @@ -92,10 +93,13 @@ namespace Ryujinx.Ava.Utilities.PlayReport "a_w24" => "South Paldean Sea", "a_w25" => "West Paldean Sea", "a_w26" => "East Paldean Sea", - "a_w27" => "Nouth Paldean Sea", + "a_w27" => "North Paldean Sea", //TODO DLC Locations _ => FormattedValue.ForceReset }; + + return$"{playStatus} in {locations}"; + } private static FormattedValue SuperSmashBrosUltimate_Mode(SparseMultiValue values) { @@ -115,6 +119,11 @@ namespace Ryujinx.Ava.Utilities.PlayReport return $"Achievement Unlocked - ID: {anniversary}"; } + if (values.Matched.ContainsKey("is_created")) + { + return "Edited a Custom Stage!"; + } + if (values.Matched.ContainsKey("adv_slot")) { return diff --git a/src/Ryujinx/Utilities/PlayReport/PlayReports.cs b/src/Ryujinx/Systems/PlayReport/PlayReports.cs similarity index 93% rename from src/Ryujinx/Utilities/PlayReport/PlayReports.cs rename to src/Ryujinx/Systems/PlayReport/PlayReports.cs index 9feb888b3..e42ca185c 100644 --- a/src/Ryujinx/Utilities/PlayReport/PlayReports.cs +++ b/src/Ryujinx/Systems/PlayReport/PlayReports.cs @@ -1,6 +1,6 @@ using System; -namespace Ryujinx.Ava.Utilities.PlayReport +namespace Ryujinx.Ava.Systems.PlayReport { public static partial class PlayReports { @@ -59,9 +59,8 @@ namespace Ryujinx.Ava.Utilities.PlayReport .AddSpec( ["0100a3d008c5c000", "01008f6008c5e000"], spec => spec - .WithDescription("based on what area of Paldea you're exploring.") - .AddValueFormatter("area_no", PokemonSVArea) - .AddValueFormatter("team_circle", PokemonSVUnionCircle) + .WithDescription("based on if you're playing alone or in a group and what area of Paldea you're exploring.") + .AddMultiValueFormatter(["team_circle", "area_no"], PokemonSV) ) .AddSpec( "01006a800016e000", @@ -71,7 +70,7 @@ namespace Ryujinx.Ava.Utilities.PlayReport [ // Metadata to figure out what PlayReport we have. "match_mode", "match_submode", "anniversary", "fighter", "reason", "challenge_count", - "adv_slot", + "adv_slot", "is_created", // List of Fighters "player_1_fighter", "player_2_fighter", "player_3_fighter", "player_4_fighter", "player_5_fighter", "player_6_fighter", "player_7_fighter", "player_8_fighter", diff --git a/src/Ryujinx/Utilities/PlayReport/Specs.cs b/src/Ryujinx/Systems/PlayReport/Specs.cs similarity index 97% rename from src/Ryujinx/Utilities/PlayReport/Specs.cs rename to src/Ryujinx/Systems/PlayReport/Specs.cs index c162d4c2c..2dd1c02ac 100644 --- a/src/Ryujinx/Utilities/PlayReport/Specs.cs +++ b/src/Ryujinx/Systems/PlayReport/Specs.cs @@ -1,10 +1,10 @@ using MsgPack; -using Ryujinx.Ava.Utilities.AppLibrary; +using Ryujinx.Ava.Systems.AppLibrary; using System; using System.Collections.Generic; using System.Linq; -namespace Ryujinx.Ava.Utilities.PlayReport +namespace Ryujinx.Ava.Systems.PlayReport { /// /// A mapping of title IDs to value formatter specs. @@ -103,7 +103,7 @@ namespace Ryujinx.Ava.Utilities.PlayReport /// matching a specific set of keys that could exist in a Play Report for the previously specified title IDs. ///

/// The 'Sparse' multi-value formatters do not require every key to be present. - /// If you need this requirement, use . + /// If you need this requirement, use . ///
/// The key names to match. /// The function which can format the values. @@ -118,7 +118,7 @@ namespace Ryujinx.Ava.Utilities.PlayReport /// matching a specific set of keys that could exist in a Play Report for the previously specified title IDs. ///

/// The 'Sparse' multi-value formatters do not require every key to be present. - /// If you need this requirement, use . + /// If you need this requirement, use . ///
/// The resolution priority of this value formatter. Higher resolves sooner. /// The key names to match. diff --git a/src/Ryujinx/Utilities/PlayReport/Value.cs b/src/Ryujinx/Systems/PlayReport/Value.cs similarity index 99% rename from src/Ryujinx/Utilities/PlayReport/Value.cs rename to src/Ryujinx/Systems/PlayReport/Value.cs index b3108a41e..1c738c213 100644 --- a/src/Ryujinx/Utilities/PlayReport/Value.cs +++ b/src/Ryujinx/Systems/PlayReport/Value.cs @@ -3,7 +3,7 @@ using System; using System.Collections.Generic; using System.Linq; -namespace Ryujinx.Ava.Utilities.PlayReport +namespace Ryujinx.Ava.Systems.PlayReport { /// /// The base input data to a ValueFormatter delegate, diff --git a/src/Ryujinx/Systems/Rebooter.cs b/src/Ryujinx/Systems/Rebooter.cs new file mode 100644 index 000000000..4b149e9e3 --- /dev/null +++ b/src/Ryujinx/Systems/Rebooter.cs @@ -0,0 +1,74 @@ +using FluentAvalonia.UI.Controls; +using Ryujinx.Ava.Common.Locale; +using Ryujinx.Ava.Utilities; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Threading.Tasks; + +namespace Ryujinx.Ava.Systems +{ + internal static class Rebooter + { + + private static readonly string _updateDir = Path.Combine(Path.GetTempPath(), "Ryujinx", "update"); + + + public static void RebootAppWithGame(string gamePath, List args) + { + _ = Reboot(gamePath, args); + + } + + private static async Task Reboot(string gamePath, List args) + { + + bool shouldRestart = true; + + TaskDialog taskDialog = new() + { + Header = LocaleManager.Instance[LocaleKeys.RyujinxRebooter], + SubHeader = LocaleManager.Instance[LocaleKeys.DialogRebooterMessage], + IconSource = new SymbolIconSource { Symbol = Symbol.Games }, + XamlRoot = RyujinxApp.MainWindow, + }; + + if (shouldRestart) + { + List arguments = CommandLineState.Arguments.ToList(); + string executableDirectory = AppDomain.CurrentDomain.BaseDirectory; + + var dialogTask = taskDialog.ShowAsync(true); + await Task.Delay(500); + + // Find the process name. + string ryuName = Path.GetFileName(Environment.ProcessPath) ?? string.Empty; + + // Fallback if the executable could not be found. + if (ryuName.Length == 0 || !Path.Exists(Path.Combine(executableDirectory, ryuName))) + { + ryuName = OperatingSystem.IsWindows() ? "Ryujinx.exe" : "Ryujinx"; + } + + ProcessStartInfo processStart = new(ryuName) + { + UseShellExecute = true, + WorkingDirectory = executableDirectory, + }; + + foreach (var arg in args) + { + processStart.ArgumentList.Add(arg); + } + + processStart.ArgumentList.Add(gamePath); + + Process.Start(processStart); + + Environment.Exit(0); + } + } + } +} diff --git a/src/Ryujinx/Updater.cs b/src/Ryujinx/Systems/Updater.cs similarity index 96% rename from src/Ryujinx/Updater.cs rename to src/Ryujinx/Systems/Updater.cs index 338e9de43..850bacc60 100644 --- a/src/Ryujinx/Updater.cs +++ b/src/Ryujinx/Systems/Updater.cs @@ -27,7 +27,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; -namespace Ryujinx.Ava +namespace Ryujinx.Ava.Systems { internal static class Updater { @@ -43,17 +43,9 @@ namespace Ryujinx.Ava private const int ConnectionCount = 4; private static string _buildVer; + - private static readonly string _platformExt = - RunningPlatform.IsMacOS - ? "macos_universal.app.tar.gz" - : RunningPlatform.IsWindows - ? "win_x64.zip" - : RunningPlatform.IsX64Linux - ? "linux_x64.tar.gz" - : RunningPlatform.IsArmLinux - ? "linux_arm64.tar.gz" - : throw new PlatformNotSupportedException(); + private static readonly string _platformExt = BuildPlatformExtension(); private static string _buildUrl; private static long _buildSize; @@ -780,5 +772,34 @@ namespace Ryujinx.Ava public static void CleanupUpdate() => Directory.GetFiles(_homeDir, "*.ryuold", SearchOption.AllDirectories) .ForEach(File.Delete); + + private static string BuildPlatformExtension() + { + if (RunningPlatform.IsMacOS) + return "macos_universal.app.tar.gz"; + +#pragma warning disable CS8509 // It is exhaustive for any values this can contain. + string osPrefix = RunningPlatform.CurrentOS switch + { + OperatingSystemType.Linux => "linux", + OperatingSystemType.Windows => "win" + }; + + string archSuffix = RunningPlatform.Architecture switch + { + Architecture.Arm64 => "arm64", + Architecture.X64 => "x64", + _ => throw new PlatformNotSupportedException($"Unknown architecture {Enum.GetName(RunningPlatform.Architecture)}."), + }; + + string fileExtension = RunningPlatform.CurrentOS switch +#pragma warning restore CS8509 + { + OperatingSystemType.Linux => "tar.gz", + OperatingSystemType.Windows => "zip" + }; + + return $"{osPrefix}_{archSuffix}.{fileExtension}"; + } } } diff --git a/src/Ryujinx/UI/Applet/AvaHostUIHandler.cs b/src/Ryujinx/UI/Applet/AvaHostUIHandler.cs index b1f1fc13d..225c45383 100644 --- a/src/Ryujinx/UI/Applet/AvaHostUIHandler.cs +++ b/src/Ryujinx/UI/Applet/AvaHostUIHandler.cs @@ -7,7 +7,7 @@ using Ryujinx.Ava.UI.Controls; using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.Windows; -using Ryujinx.Ava.Utilities.Configuration; +using Ryujinx.Ava.Systems.Configuration; using Ryujinx.Common; using Ryujinx.HLE; using Ryujinx.HLE.HOS.Applets; @@ -96,7 +96,7 @@ namespace Ryujinx.Ava.UI.Applet _parent.SettingsWindow = new SettingsWindow(_parent.VirtualFileSystem, _parent.ContentManager); - await _parent.SettingsWindow.ShowDialog(window); + await StyleableAppWindow.ShowAsync(_parent.SettingsWindow, window); _parent.SettingsWindow = null; diff --git a/src/Ryujinx/UI/Applet/ProfileSelectorDialog.axaml b/src/Ryujinx/UI/Applet/ProfileSelectorDialog.axaml index d929cc501..20d466031 100644 --- a/src/Ryujinx/UI/Applet/ProfileSelectorDialog.axaml +++ b/src/Ryujinx/UI/Applet/ProfileSelectorDialog.axaml @@ -17,12 +17,8 @@ - - - - - - + + { - public ProfileSelectorDialogViewModel ViewModel { get; set; } - public ProfileSelectorDialog(ProfileSelectorDialogViewModel viewModel) { DataContext = ViewModel = viewModel; diff --git a/src/Ryujinx/UI/Controls/ApplicationContextMenu.axaml b/src/Ryujinx/UI/Controls/ApplicationContextMenu.axaml index 3e47a1910..64b30e211 100644 --- a/src/Ryujinx/UI/Controls/ApplicationContextMenu.axaml +++ b/src/Ryujinx/UI/Controls/ApplicationContextMenu.axaml @@ -19,6 +19,18 @@ Header="{ext:Locale GameListContextMenuCreateShortcut}" Icon="{ext:Icon fa-solid fa-bookmark}" ToolTip.Tip="{OnPlatform Default={ext:Locale GameListContextMenuCreateShortcutToolTip}, macOS={ext:Locale GameListContextMenuCreateShortcutToolTipMacOS}}" /> + + 0) @@ -386,13 +390,26 @@ namespace Ryujinx.Ava.UI.Controls viewModel.SelectedApplication.Icon ); } - + + public async void EditGameConfiguration_Click(object sender, RoutedEventArgs args) + { + if (sender is MenuItem { DataContext: MainWindowViewModel { SelectedApplication: not null } viewModel }) + { + await StyleableAppWindow.ShowAsync(new GameSpecificSettingsWindow(viewModel)); + + // just checking for file presence + viewModel.SelectedApplication.HasIndependentConfiguration = File.Exists(Program.GetDirGameUserConfig(viewModel.SelectedApplication.IdString,false,false)); + + viewModel.RefreshView(); + } + } + public async void OpenApplicationCompatibility_Click(object sender, RoutedEventArgs args) { if (sender is MenuItem { DataContext: MainWindowViewModel { SelectedApplication: not null } viewModel }) - await CompatibilityList.Show(viewModel.SelectedApplication.IdString); + await CompatibilityListWindow.Show(viewModel.SelectedApplication.IdString); } - + public async void OpenApplicationData_Click(object sender, RoutedEventArgs args) { if (sender is MenuItem { DataContext: MainWindowViewModel { SelectedApplication: not null } viewModel }) diff --git a/src/Ryujinx/UI/Controls/StatusBarSeparator.cs b/src/Ryujinx/UI/Controls/MiniVerticalSeparator.cs similarity index 100% rename from src/Ryujinx/UI/Controls/StatusBarSeparator.cs rename to src/Ryujinx/UI/Controls/MiniVerticalSeparator.cs diff --git a/src/Ryujinx/UI/Controls/NavigationDialogHost.axaml.cs b/src/Ryujinx/UI/Controls/NavigationDialogHost.axaml.cs index fdfc588e2..12c6a9daf 100644 --- a/src/Ryujinx/UI/Controls/NavigationDialogHost.axaml.cs +++ b/src/Ryujinx/UI/Controls/NavigationDialogHost.axaml.cs @@ -23,13 +23,12 @@ using UserProfile = Ryujinx.Ava.UI.Models.UserProfile; namespace Ryujinx.Ava.UI.Controls { - public partial class NavigationDialogHost : UserControl + public partial class NavigationDialogHost : RyujinxControl { public AccountManager AccountManager { get; } public ContentManager ContentManager { get; } public VirtualFileSystem VirtualFileSystem { get; } public HorizonClient HorizonClient { get; } - public UserProfileViewModel ViewModel { get; set; } public NavigationDialogHost() { diff --git a/src/Ryujinx/UI/Controls/RyujinxControl.cs b/src/Ryujinx/UI/Controls/RyujinxControl.cs new file mode 100644 index 000000000..748d7ed94 --- /dev/null +++ b/src/Ryujinx/UI/Controls/RyujinxControl.cs @@ -0,0 +1,24 @@ +using Avalonia.Controls; +using Gommon; +using Ryujinx.Ava.UI.ViewModels; +using System; + +namespace Ryujinx.Ava.UI.Controls +{ + public class RyujinxControl : UserControl where TViewModel : BaseModel + { + public TViewModel ViewModel + { + get + { + if (DataContext is not TViewModel viewModel) + throw new InvalidOperationException( + $"Underlying DataContext is not of type {typeof(TViewModel).AsPrettyString()}; " + + $"Actual type is {DataContext?.GetType().AsPrettyString()}"); + + return viewModel; + } + set => DataContext = value; + } + } +} diff --git a/src/Ryujinx/UI/Controls/RyujinxLogo.cs b/src/Ryujinx/UI/Controls/RyujinxLogo.cs new file mode 100644 index 000000000..89b89f311 --- /dev/null +++ b/src/Ryujinx/UI/Controls/RyujinxLogo.cs @@ -0,0 +1,28 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Media.Imaging; +using Ryujinx.Ava.Systems.Configuration; +using Ryujinx.Ava.UI.ViewModels; +using System.Reflection; + +namespace Ryujinx.Ava.UI.Controls +{ + public class RyujinxLogo : Image + { + // The UI specifically uses a thicker bordered variant of the icon to avoid crunching out the border at lower resolutions. + // For an example of this, download canary 1.2.95, then open the settings menu, and look at the icon in the top-left. + // The border gets reduced to colored pixels in the 4 corners. + public static readonly Bitmap Bitmap = + new(Assembly.GetAssembly(typeof(MainWindowViewModel))! + .GetManifestResourceStream("Ryujinx.Assets.UIImages.Logo_Ryujinx_AntiAlias.png")!); + + public RyujinxLogo() + { + Margin = new Thickness(7, 7, 7, 0); + Height = 25; + Width = 25; + Source = Bitmap; + IsVisible = !ConfigurationState.Instance.ShowOldUI; + } + } +} diff --git a/src/Ryujinx/UI/Helpers/ApplicationOpenedEventArgs.cs b/src/Ryujinx/UI/Helpers/ApplicationOpenedEventArgs.cs index 0ceaa6c4c..93f6b4c3c 100644 --- a/src/Ryujinx/UI/Helpers/ApplicationOpenedEventArgs.cs +++ b/src/Ryujinx/UI/Helpers/ApplicationOpenedEventArgs.cs @@ -1,5 +1,5 @@ using Avalonia.Interactivity; -using Ryujinx.Ava.Utilities.AppLibrary; +using Ryujinx.Ava.Systems.AppLibrary; namespace Ryujinx.Ava.UI.Helpers { diff --git a/src/Ryujinx/UI/Helpers/ContentDialogHelper.cs b/src/Ryujinx/UI/Helpers/ContentDialogHelper.cs index ae83f9e98..a93b2894e 100644 --- a/src/Ryujinx/UI/Helpers/ContentDialogHelper.cs +++ b/src/Ryujinx/UI/Helpers/ContentDialogHelper.cs @@ -1,8 +1,10 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Input; using Avalonia.Layout; using Avalonia.Media; +using Avalonia.Styling; using Avalonia.Threading; using FluentAvalonia.Core; using FluentAvalonia.UI.Controls; @@ -20,6 +22,23 @@ namespace Ryujinx.Ava.UI.Helpers private static bool _isChoiceDialogOpen; private static ContentDialogOverlayWindow _contentDialogOverlayWindow; + public static ContentDialog ApplyStyles( + this ContentDialog contentDialog, + double closeButtonWidth = 80, + HorizontalAlignment buttonSpaceAlignment = HorizontalAlignment.Right) + { + Style closeButton = new(x => x.Name("CloseButton")); + closeButton.Setters.Add(new Setter(Layoutable.WidthProperty, closeButtonWidth)); + + Style closeButtonParent = new(x => x.Name("CommandSpace")); + closeButtonParent.Setters.Add(new Setter(Layoutable.HorizontalAlignmentProperty, buttonSpaceAlignment)); + + contentDialog.Styles.Add(closeButton); + contentDialog.Styles.Add(closeButtonParent); + + return contentDialog; + } + private async static Task ShowContentDialog( string title, object content, @@ -39,19 +58,19 @@ namespace Ryujinx.Ava.UI.Helpers SecondaryButtonText = secondaryButton, CloseButtonText = closeButton, Content = content, - PrimaryButtonCommand = MiniCommand.Create(() => + PrimaryButtonCommand = Commands.Create(() => { result = primaryButtonResult; }) }; - contentDialog.SecondaryButtonCommand = MiniCommand.Create(() => + contentDialog.SecondaryButtonCommand = Commands.Create(() => { result = UserResult.No; contentDialog.PrimaryButtonClick -= deferCloseAction; }); - contentDialog.CloseButtonCommand = MiniCommand.Create(() => + contentDialog.CloseButtonCommand = Commands.Create(() => { result = UserResult.Cancel; contentDialog.PrimaryButtonClick -= deferCloseAction; @@ -384,6 +403,10 @@ namespace Ryujinx.Ava.UI.Helpers Position = parent.PointToScreen(new Point()), ShowInTaskbar = false, }; + +#if DEBUG + _contentDialogOverlayWindow.AttachDevTools(new KeyGesture(Key.F12, KeyModifiers.Control)); +#endif parent.PositionChanged += OverlayOnPositionChanged; diff --git a/src/Ryujinx/UI/Helpers/Converters/MultiplayerInfoConverter.cs b/src/Ryujinx/UI/Helpers/Converters/MultiplayerInfoConverter.cs index 7694e8883..1dca0d164 100644 --- a/src/Ryujinx/UI/Helpers/Converters/MultiplayerInfoConverter.cs +++ b/src/Ryujinx/UI/Helpers/Converters/MultiplayerInfoConverter.cs @@ -2,7 +2,7 @@ using Avalonia.Data.Converters; using Avalonia.Markup.Xaml; using Gommon; using Ryujinx.Ava.Common.Locale; -using Ryujinx.Ava.Utilities.AppLibrary; +using Ryujinx.Ava.Systems.AppLibrary; using System; using System.Globalization; using System.Text; diff --git a/src/Ryujinx/UI/Windows/IconColorPicker.cs b/src/Ryujinx/UI/Helpers/IconColorPicker.cs similarity index 98% rename from src/Ryujinx/UI/Windows/IconColorPicker.cs rename to src/Ryujinx/UI/Helpers/IconColorPicker.cs index ca9ac2c05..b6ee8bbbf 100644 --- a/src/Ryujinx/UI/Windows/IconColorPicker.cs +++ b/src/Ryujinx/UI/Helpers/IconColorPicker.cs @@ -2,9 +2,9 @@ using SkiaSharp; using System; using System.Collections.Generic; -namespace Ryujinx.Ava.UI.Windows +namespace Ryujinx.Ava.UI.Helpers { - static class IconColorPicker + public static class IconColorPicker { private const int ColorsPerLine = 64; private const int TotalColors = ColorsPerLine * ColorsPerLine; diff --git a/src/Ryujinx/UI/Helpers/LoggerAdapter.cs b/src/Ryujinx/UI/Helpers/LoggerAdapter.cs index ba317e74a..902d3966f 100644 --- a/src/Ryujinx/UI/Helpers/LoggerAdapter.cs +++ b/src/Ryujinx/UI/Helpers/LoggerAdapter.cs @@ -1,7 +1,7 @@ using Avalonia.Logging; using Avalonia.Utilities; using Gommon; -using Ryujinx.Ava.Utilities.Configuration; +using Ryujinx.Ava.Systems.Configuration; using Ryujinx.Common.Logging; using System; using System.Text; diff --git a/src/Ryujinx/UI/Helpers/MiniCommand.cs b/src/Ryujinx/UI/Helpers/MiniCommand.cs deleted file mode 100644 index 9782aa69d..000000000 --- a/src/Ryujinx/UI/Helpers/MiniCommand.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System; -using System.Threading.Tasks; -using System.Windows.Input; - -namespace Ryujinx.Ava.UI.Helpers -{ - public sealed class MiniCommand : MiniCommand, ICommand - { - private readonly Action _callback; - private bool _busy; - private readonly Func _asyncCallback; - - public MiniCommand(Action callback) - { - _callback = callback; - } - - public MiniCommand(Func callback) - { - _asyncCallback = callback; - } - - private bool Busy - { - get => _busy; - set - { - _busy = value; - CanExecuteChanged?.Invoke(this, EventArgs.Empty); - } - } - - public override event EventHandler CanExecuteChanged; - public override bool CanExecute(object parameter) => !_busy; - - public override async void Execute(object parameter) - { - if (Busy) - { - return; - } - try - { - Busy = true; - if (_callback != null) - { - _callback((T)parameter); - } - else - { - await _asyncCallback((T)parameter); - } - } - finally - { - Busy = false; - } - } - } - - public abstract class MiniCommand : ICommand - { - public static MiniCommand Create(Action callback) => new MiniCommand(_ => callback()); - public static MiniCommand Create(Action callback) => new MiniCommand(callback); - public static MiniCommand CreateFromTask(Func callback) => new MiniCommand(_ => callback()); - public static MiniCommand CreateFromTask(Func callback) => new MiniCommand(callback); - - public abstract bool CanExecute(object parameter); - public abstract void Execute(object parameter); - public abstract event EventHandler CanExecuteChanged; - } -} diff --git a/src/Ryujinx/UI/Models/Generic/LastPlayedSortComparer.cs b/src/Ryujinx/UI/Models/Generic/LastPlayedSortComparer.cs index f2d27f2df..3808ae6a2 100644 --- a/src/Ryujinx/UI/Models/Generic/LastPlayedSortComparer.cs +++ b/src/Ryujinx/UI/Models/Generic/LastPlayedSortComparer.cs @@ -1,4 +1,4 @@ -using Ryujinx.Ava.Utilities.AppLibrary; +using Ryujinx.Ava.Systems.AppLibrary; using System; using System.Collections.Generic; diff --git a/src/Ryujinx/UI/Models/Generic/TimePlayedSortComparer.cs b/src/Ryujinx/UI/Models/Generic/TimePlayedSortComparer.cs index d7ae51e96..321bde09b 100644 --- a/src/Ryujinx/UI/Models/Generic/TimePlayedSortComparer.cs +++ b/src/Ryujinx/UI/Models/Generic/TimePlayedSortComparer.cs @@ -1,4 +1,4 @@ -using Ryujinx.Ava.Utilities.AppLibrary; +using Ryujinx.Ava.Systems.AppLibrary; using System; using System.Collections.Generic; diff --git a/src/Ryujinx/UI/Models/Input/StickVisualizer.cs b/src/Ryujinx/UI/Models/Input/StickVisualizer.cs new file mode 100644 index 000000000..b7e9ec331 --- /dev/null +++ b/src/Ryujinx/UI/Models/Input/StickVisualizer.cs @@ -0,0 +1,260 @@ +using Ryujinx.Ava.UI.ViewModels; +using Ryujinx.Ava.UI.ViewModels.Input; +using Ryujinx.Input; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Ryujinx.Ava.UI.Models.Input +{ + public class StickVisualizer : BaseModel, IDisposable + { + public const int DrawStickPollRate = 50; // Milliseconds per poll. + public const int DrawStickCircumference = 5; + public const float DrawStickScaleFactor = DrawStickCanvasCenter; + public const int DrawStickCanvasSize = 100; + public const int DrawStickBorderSize = DrawStickCanvasSize + 5; + public const float DrawStickCanvasCenter = (DrawStickCanvasSize - DrawStickCircumference) / 2; + public const float MaxVectorLength = DrawStickCanvasSize / 2; + + public CancellationTokenSource PollTokenSource; + public CancellationToken PollToken; + + private static float _vectorLength; + private static float _vectorMultiplier; + + private bool disposedValue; + + private DeviceType _type; + public DeviceType Type + { + get => _type; + set + { + _type = value; + + OnPropertyChanged(); + } + } + + private GamepadInputConfig _gamepadConfig; + public GamepadInputConfig GamepadConfig + { + get => _gamepadConfig; + set + { + _gamepadConfig = value; + + OnPropertyChanged(); + } + } + + private KeyboardInputConfig _keyboardConfig; + public KeyboardInputConfig KeyboardConfig + { + get => _keyboardConfig; + set + { + _keyboardConfig = value; + + OnPropertyChanged(); + } + } + + private (float, float) _uiStickLeft; + public (float, float) UiStickLeft + { + get => (_uiStickLeft.Item1 * DrawStickScaleFactor, _uiStickLeft.Item2 * DrawStickScaleFactor); + set + { + _uiStickLeft = value; + + OnPropertyChanged(); + OnPropertyChanged(nameof(UiStickRightX)); + OnPropertyChanged(nameof(UiStickRightY)); + OnPropertyChanged(nameof(UiDeadzoneRight)); + } + } + + private (float, float) _uiStickRight; + public (float, float) UiStickRight + { + get => (_uiStickRight.Item1 * DrawStickScaleFactor, _uiStickRight.Item2 * DrawStickScaleFactor); + set + { + _uiStickRight = value; + + OnPropertyChanged(); + OnPropertyChanged(nameof(UiStickLeftX)); + OnPropertyChanged(nameof(UiStickLeftY)); + OnPropertyChanged(nameof(UiDeadzoneLeft)); + } + } + + public float UiStickLeftX => ClampVector(UiStickLeft).Item1; + public float UiStickLeftY => ClampVector(UiStickLeft).Item2; + public float UiStickRightX => ClampVector(UiStickRight).Item1; + public float UiStickRightY => ClampVector(UiStickRight).Item2; + + public int UiStickCircumference => DrawStickCircumference; + public int UiCanvasSize => DrawStickCanvasSize; + public int UiStickBorderSize => DrawStickBorderSize; + + public float? UiDeadzoneLeft => _gamepadConfig?.DeadzoneLeft * DrawStickCanvasSize - DrawStickCircumference; + public float? UiDeadzoneRight => _gamepadConfig?.DeadzoneRight * DrawStickCanvasSize - DrawStickCircumference; + + private InputViewModel Parent; + + public StickVisualizer(InputViewModel parent) + { + Parent = parent; + + PollTokenSource = new CancellationTokenSource(); + PollToken = PollTokenSource.Token; + + Task.Run(Initialize, PollToken); + } + + public void UpdateConfig(object config) + { + if (config is ControllerInputViewModel padConfig) + { + GamepadConfig = padConfig.Config; + Type = DeviceType.Controller; + + return; + } + else if (config is KeyboardInputViewModel keyConfig) + { + KeyboardConfig = keyConfig.Config; + Type = DeviceType.Keyboard; + + return; + } + + Type = DeviceType.None; + } + + public async Task Initialize() + { + (float, float) leftBuffer; + (float, float) rightBuffer; + + while (!PollToken.IsCancellationRequested) + { + leftBuffer = (0f, 0f); + rightBuffer = (0f, 0f); + + switch (Type) + { + case DeviceType.Keyboard: + IKeyboard keyboard = (IKeyboard)Parent.AvaloniaKeyboardDriver.GetGamepad("0"); + + if (keyboard != null) + { + KeyboardStateSnapshot snapshot = keyboard.GetKeyboardStateSnapshot(); + + if (snapshot.IsPressed((Key)KeyboardConfig.LeftStickRight)) + { + leftBuffer.Item1 += 1; + } + if (snapshot.IsPressed((Key)KeyboardConfig.LeftStickLeft)) + { + leftBuffer.Item1 -= 1; + } + if (snapshot.IsPressed((Key)KeyboardConfig.LeftStickUp)) + { + leftBuffer.Item2 += 1; + } + if (snapshot.IsPressed((Key)KeyboardConfig.LeftStickDown)) + { + leftBuffer.Item2 -= 1; + } + + if (snapshot.IsPressed((Key)KeyboardConfig.RightStickRight)) + { + rightBuffer.Item1 += 1; + } + if (snapshot.IsPressed((Key)KeyboardConfig.RightStickLeft)) + { + rightBuffer.Item1 -= 1; + } + if (snapshot.IsPressed((Key)KeyboardConfig.RightStickUp)) + { + rightBuffer.Item2 += 1; + } + if (snapshot.IsPressed((Key)KeyboardConfig.RightStickDown)) + { + rightBuffer.Item2 -= 1; + } + + UiStickLeft = leftBuffer; + UiStickRight = rightBuffer; + } + break; + + case DeviceType.Controller: + IGamepad controller = Parent.SelectedGamepad; + + if (controller != null) + { + leftBuffer = controller.GetStick((StickInputId)GamepadConfig.LeftJoystick); + rightBuffer = controller.GetStick((StickInputId)GamepadConfig.RightJoystick); + } + break; + + case DeviceType.None: + break; + default: + throw new ArgumentException($"Unable to poll device type \"{Type}\""); + } + + UiStickLeft = leftBuffer; + UiStickRight = rightBuffer; + + await Task.Delay(DrawStickPollRate, PollToken); + } + + PollTokenSource.Dispose(); + } + + public static (float, float) ClampVector((float, float) vect) + { + _vectorMultiplier = 1; + _vectorLength = MathF.Sqrt((vect.Item1 * vect.Item1) + (vect.Item2 * vect.Item2)); + + if (_vectorLength > MaxVectorLength) + { + _vectorMultiplier = MaxVectorLength / _vectorLength; + } + + vect.Item1 = vect.Item1 * _vectorMultiplier + DrawStickCanvasCenter; + vect.Item2 = vect.Item2 * _vectorMultiplier + DrawStickCanvasCenter; + + return vect; + } + + protected virtual void Dispose(bool disposing) + { + if (!disposedValue) + { + if (disposing) + { + PollTokenSource.Cancel(); + } + + KeyboardConfig = null; + GamepadConfig = null; + Parent = null; + + disposedValue = true; + } + } + + public void Dispose() + { + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + } +} diff --git a/src/Ryujinx/UI/Models/SaveModel.cs b/src/Ryujinx/UI/Models/SaveModel.cs index d50aabc4e..1a4718ddf 100644 --- a/src/Ryujinx/UI/Models/SaveModel.cs +++ b/src/Ryujinx/UI/Models/SaveModel.cs @@ -3,7 +3,7 @@ using LibHac.Fs; using LibHac.Ncm; using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.Utilities; -using Ryujinx.Ava.Utilities.AppLibrary; +using Ryujinx.Ava.Systems.AppLibrary; using Ryujinx.HLE.FileSystem; using System.IO; using System.Linq; diff --git a/src/Ryujinx/UI/Renderer/EmbeddedWindow.cs b/src/Ryujinx/UI/Renderer/EmbeddedWindow.cs index 21c39967f..7b642bb9d 100644 --- a/src/Ryujinx/UI/Renderer/EmbeddedWindow.cs +++ b/src/Ryujinx/UI/Renderer/EmbeddedWindow.cs @@ -1,7 +1,7 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Platform; -using Ryujinx.Ava.Utilities.Configuration; +using Ryujinx.Ava.Systems.Configuration; using Ryujinx.Common.Configuration; using Ryujinx.Common.Helper; using SPB.Graphics; diff --git a/src/Ryujinx/UI/Renderer/EmbeddedWindowMetal.cs b/src/Ryujinx/UI/Renderer/EmbeddedWindowMetal.cs deleted file mode 100644 index 9e92d9289..000000000 --- a/src/Ryujinx/UI/Renderer/EmbeddedWindowMetal.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Ryujinx.Common.Helper; -using SharpMetal.QuartzCore; -using System; - -namespace Ryujinx.Ava.UI.Renderer -{ - public class EmbeddedWindowMetal : EmbeddedWindow - { - public CAMetalLayer CreateSurface() - { - if (OperatingSystem.IsMacOS() && RunningPlatform.IsArm) - { - return new CAMetalLayer(MetalLayer); - } - - throw new NotSupportedException($"Cannot create a {nameof(CAMetalLayer)} without being on ARM Mac."); - } - } -} diff --git a/src/Ryujinx/UI/Renderer/EmbeddedWindowOpenGL.cs b/src/Ryujinx/UI/Renderer/EmbeddedWindowOpenGL.cs index 81a94d6c4..e788272f6 100644 --- a/src/Ryujinx/UI/Renderer/EmbeddedWindowOpenGL.cs +++ b/src/Ryujinx/UI/Renderer/EmbeddedWindowOpenGL.cs @@ -1,5 +1,5 @@ using OpenTK.Graphics.OpenGL; -using Ryujinx.Ava.Utilities.Configuration; +using Ryujinx.Ava.Systems.Configuration; using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; using Ryujinx.Graphics.GAL; diff --git a/src/Ryujinx/UI/Renderer/RendererHost.cs b/src/Ryujinx/UI/Renderer/RendererHost.cs index f755b6d70..fd7124f2b 100644 --- a/src/Ryujinx/UI/Renderer/RendererHost.cs +++ b/src/Ryujinx/UI/Renderer/RendererHost.cs @@ -1,10 +1,8 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Media; -using Ryujinx.Ava.Utilities.Configuration; -using Ryujinx.Common; +using Ryujinx.Ava.Systems.Configuration; using Ryujinx.Common.Configuration; -using Ryujinx.Common.Logging; using System; namespace Ryujinx.Ava.UI.Renderer @@ -24,8 +22,7 @@ namespace Ryujinx.Ava.UI.Renderer EmbeddedWindow = ConfigurationState.Instance.Graphics.GraphicsBackend.Value switch { GraphicsBackend.OpenGl => new EmbeddedWindowOpenGL(), - GraphicsBackend.Metal => new EmbeddedWindowMetal(), - GraphicsBackend.Vulkan or GraphicsBackend.Auto => new EmbeddedWindowVulkan(), + GraphicsBackend.Vulkan => new EmbeddedWindowVulkan(), _ => throw new NotSupportedException() }; @@ -37,37 +34,8 @@ namespace Ryujinx.Ava.UI.Renderer { EmbeddedWindowVulkan => GraphicsBackend.Vulkan, EmbeddedWindowOpenGL => GraphicsBackend.OpenGl, - EmbeddedWindowMetal => GraphicsBackend.Metal, _ => throw new NotImplementedException() }; - - public RendererHost(string titleId) - { - Focusable = true; - FlowDirection = FlowDirection.LeftToRight; - - EmbeddedWindow = -#pragma warning disable CS8509 - TitleIDs.SelectGraphicsBackend(titleId, ConfigurationState.Instance.Graphics.GraphicsBackend) switch -#pragma warning restore CS8509 - { - GraphicsBackend.OpenGl => new EmbeddedWindowOpenGL(), - GraphicsBackend.Metal => new EmbeddedWindowMetal(), - GraphicsBackend.Vulkan => new EmbeddedWindowVulkan(), - }; - - string backendText = EmbeddedWindow switch - { - EmbeddedWindowVulkan => "Vulkan", - EmbeddedWindowOpenGL => "OpenGL", - EmbeddedWindowMetal => "Metal", - _ => throw new NotImplementedException() - }; - - Logger.Info?.PrintMsg(LogClass.Gpu, $"Backend ({ConfigurationState.Instance.Graphics.GraphicsBackend.Value}): {backendText}"); - - Initialize(); - } private void Initialize() @@ -107,4 +75,3 @@ namespace Ryujinx.Ava.UI.Renderer } } } - diff --git a/src/Ryujinx/RyujinxApp.axaml b/src/Ryujinx/UI/RyujinxApp.axaml similarity index 84% rename from src/Ryujinx/RyujinxApp.axaml rename to src/Ryujinx/UI/RyujinxApp.axaml index aca69645a..bffb5cece 100644 --- a/src/Ryujinx/RyujinxApp.axaml +++ b/src/Ryujinx/UI/RyujinxApp.axaml @@ -17,4 +17,9 @@ + + + + + diff --git a/src/Ryujinx/RyujinxApp.axaml.cs b/src/Ryujinx/UI/RyujinxApp.axaml.cs similarity index 96% rename from src/Ryujinx/RyujinxApp.axaml.cs rename to src/Ryujinx/UI/RyujinxApp.axaml.cs index 90552cd16..2f460f2b3 100644 --- a/src/Ryujinx/RyujinxApp.axaml.cs +++ b/src/Ryujinx/UI/RyujinxApp.axaml.cs @@ -7,12 +7,12 @@ using Avalonia.Styling; using Avalonia.Threading; using FluentAvalonia.UI.Windowing; using Gommon; -using Ryujinx.Ava.Common; using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Windows; using Ryujinx.Ava.Utilities; -using Ryujinx.Ava.Utilities.Configuration; +using Ryujinx.Ava.Systems.Configuration; +using Ryujinx.Ava.UI.Views.Dialog; using Ryujinx.Common; using Ryujinx.Common.Logging; using System; @@ -147,5 +147,10 @@ namespace Ryujinx.Ava Current is RyujinxApp { PlatformSettings: not null } app ? ConvertThemeVariant(app.PlatformSettings.GetColorValues().ThemeVariant) : ThemeVariant.Default; + + private async void AboutRyujinx_OnClick(object sender, EventArgs e) + { + await AboutView.Show(); + } } } diff --git a/src/Ryujinx/UI/ViewModels/AboutWindowViewModel.cs b/src/Ryujinx/UI/ViewModels/AboutWindowViewModel.cs index 7a63c3391..6e1bd7ce3 100644 --- a/src/Ryujinx/UI/ViewModels/AboutWindowViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/AboutWindowViewModel.cs @@ -3,9 +3,8 @@ using Avalonia.Styling; using Avalonia.Threading; using CommunityToolkit.Mvvm.ComponentModel; using Gommon; -using Ryujinx.Ava.Common; using Ryujinx.Ava.Common.Locale; -using Ryujinx.Ava.Utilities.Configuration; +using Ryujinx.Ava.Systems.Configuration; using System; namespace Ryujinx.Ava.UI.ViewModels diff --git a/src/Ryujinx/UI/ViewModels/AppListFavoriteComparable.cs b/src/Ryujinx/UI/ViewModels/AppListFavoriteComparable.cs index 9c37368de..c8d648b99 100644 --- a/src/Ryujinx/UI/ViewModels/AppListFavoriteComparable.cs +++ b/src/Ryujinx/UI/ViewModels/AppListFavoriteComparable.cs @@ -1,4 +1,4 @@ -using Ryujinx.Ava.Utilities.AppLibrary; +using Ryujinx.Ava.Systems.AppLibrary; using System; namespace Ryujinx.Ava.UI.ViewModels diff --git a/src/Ryujinx/UI/ViewModels/ApplicationDataViewModel.cs b/src/Ryujinx/UI/ViewModels/ApplicationDataViewModel.cs index 33c75bc62..4bffcb7d3 100644 --- a/src/Ryujinx/UI/ViewModels/ApplicationDataViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/ApplicationDataViewModel.cs @@ -1,7 +1,7 @@ using Gommon; using Ryujinx.Ava.Common.Locale; -using Ryujinx.Ava.Utilities.AppLibrary; -using Ryujinx.Ava.Utilities.PlayReport; +using Ryujinx.Ava.Systems.AppLibrary; +using Ryujinx.Ava.Systems.PlayReport; namespace Ryujinx.Ava.UI.ViewModels { diff --git a/src/Ryujinx/Utilities/Compat/CompatibilityViewModel.cs b/src/Ryujinx/UI/ViewModels/CompatibilityViewModel.cs similarity index 63% rename from src/Ryujinx/Utilities/Compat/CompatibilityViewModel.cs rename to src/Ryujinx/UI/ViewModels/CompatibilityViewModel.cs index 721a36194..1965fee69 100644 --- a/src/Ryujinx/Utilities/Compat/CompatibilityViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/CompatibilityViewModel.cs @@ -1,16 +1,17 @@ using Gommon; -using Ryujinx.Ava.UI.ViewModels; -using Ryujinx.Ava.Utilities.AppLibrary; +using Ryujinx.Ava.Systems; +using Ryujinx.Ava.Systems.AppLibrary; +using System; using System.Collections.Generic; using System.Linq; -namespace Ryujinx.Ava.Utilities.Compat +namespace Ryujinx.Ava.UI.ViewModels { - public class CompatibilityViewModel : BaseModel + public class CompatibilityViewModel : BaseModel, IDisposable { - private bool _onlyShowOwnedGames = true; + private readonly ApplicationLibrary _appLibrary; - private IEnumerable _currentEntries = CompatibilityCsv.Entries; + private IEnumerable _currentEntries = CompatibilityDatabase.Entries; private string[] _ownedGameTitleIds = []; public IEnumerable CurrentEntries => OnlyShowOwnedGames @@ -19,15 +20,27 @@ namespace Ryujinx.Ava.Utilities.Compat : _currentEntries; public CompatibilityViewModel() {} + + private void AppCountUpdated(object _, ApplicationCountUpdatedEventArgs __) + => _ownedGameTitleIds = _appLibrary.Applications.Keys.Select(x => x.ToString("X16")).ToArray(); public CompatibilityViewModel(ApplicationLibrary appLibrary) { - appLibrary.ApplicationCountUpdated += (_, _) - => _ownedGameTitleIds = appLibrary.Applications.Keys.Select(x => x.ToString("X16")).ToArray(); + _appLibrary = appLibrary; - _ownedGameTitleIds = appLibrary.Applications.Keys.Select(x => x.ToString("X16")).ToArray(); + AppCountUpdated(null, null); + + _appLibrary.ApplicationCountUpdated += AppCountUpdated; } + void IDisposable.Dispose() + { + GC.SuppressFinalize(this); + _appLibrary.ApplicationCountUpdated -= AppCountUpdated; + } + + private bool _onlyShowOwnedGames = true; + public bool OnlyShowOwnedGames { get => _onlyShowOwnedGames; @@ -45,11 +58,11 @@ namespace Ryujinx.Ava.Utilities.Compat { if (string.IsNullOrEmpty(searchTerm)) { - SetEntries(CompatibilityCsv.Entries); + SetEntries(CompatibilityDatabase.Entries); return; } - SetEntries(CompatibilityCsv.Entries.Where(x => + SetEntries(CompatibilityDatabase.Entries.Where(x => x.GameName.ContainsIgnoreCase(searchTerm) || x.TitleId.Check(tid => tid.ContainsIgnoreCase(searchTerm)))); } diff --git a/src/Ryujinx/UI/ViewModels/DlcSelectViewModel.cs b/src/Ryujinx/UI/ViewModels/DlcSelectViewModel.cs index b486aa766..e236ac737 100644 --- a/src/Ryujinx/UI/ViewModels/DlcSelectViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/DlcSelectViewModel.cs @@ -1,6 +1,6 @@ using CommunityToolkit.Mvvm.ComponentModel; using Ryujinx.Ava.Common.Models; -using Ryujinx.Ava.Utilities.AppLibrary; +using Ryujinx.Ava.Systems.AppLibrary; using System.Linq; namespace Ryujinx.Ava.UI.ViewModels diff --git a/src/Ryujinx/UI/ViewModels/DownloadableContentManagerViewModel.cs b/src/Ryujinx/UI/ViewModels/DownloadableContentManagerViewModel.cs index a16a06ff5..dc1e928f1 100644 --- a/src/Ryujinx/UI/ViewModels/DownloadableContentManagerViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/DownloadableContentManagerViewModel.cs @@ -7,7 +7,7 @@ using FluentAvalonia.UI.Controls; using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Models; using Ryujinx.Ava.UI.Helpers; -using Ryujinx.Ava.Utilities.AppLibrary; +using Ryujinx.Ava.Systems.AppLibrary; using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; diff --git a/src/Ryujinx/UI/ViewModels/Input/ControllerInputViewModel.cs b/src/Ryujinx/UI/ViewModels/Input/ControllerInputViewModel.cs index 2b644cffa..f8be4b2ce 100644 --- a/src/Ryujinx/UI/ViewModels/Input/ControllerInputViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/Input/ControllerInputViewModel.cs @@ -10,8 +10,30 @@ namespace Ryujinx.Ava.UI.ViewModels.Input { public partial class ControllerInputViewModel : BaseModel { - [ObservableProperty] private GamepadInputConfig _config; + private GamepadInputConfig _config; + public GamepadInputConfig Config + { + get => _config; + set + { + _config = value; + OnPropertyChanged(); + } + } + + private StickVisualizer _visualizer; + public StickVisualizer Visualizer + { + get => _visualizer; + set + { + _visualizer = value; + + OnPropertyChanged(); + } + } + private bool _isLeft; public bool IsLeft { @@ -37,14 +59,15 @@ namespace Ryujinx.Ava.UI.ViewModels.Input } public bool HasSides => IsLeft ^ IsRight; - + [ObservableProperty] private SvgImage _image; - + public InputViewModel ParentModel { get; } - - public ControllerInputViewModel(InputViewModel model, GamepadInputConfig config) + + public ControllerInputViewModel(InputViewModel model, GamepadInputConfig config, StickVisualizer visualizer) { ParentModel = model; + Visualizer = visualizer; model.NotifyChangesEvent += OnParentModelChanged; OnParentModelChanged(); config.PropertyChanged += (_, args) => diff --git a/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs b/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs index 70ecfe373..a798d46e8 100644 --- a/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs @@ -10,7 +10,7 @@ using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Models; using Ryujinx.Ava.UI.Models.Input; using Ryujinx.Ava.UI.Windows; -using Ryujinx.Ava.Utilities.Configuration; +using Ryujinx.Ava.Systems.Configuration; using Ryujinx.Common; using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration.Hid; @@ -49,7 +49,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input private int _controller; private string _controllerImage; private int _device; - [ObservableProperty] private object _configViewModel; + private object _configViewModel; [ObservableProperty] private string _profileName; private bool _isLoaded; @@ -74,6 +74,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input OnPropertiesChanged(nameof(HasLed), nameof(CanClearLed)); } } + public StickVisualizer VisualStick { get; private set; } public ObservableCollection PlayerIndexes { get; set; } public ObservableCollection<(DeviceType Type, string Id, string Name)> Devices { get; set; } @@ -94,6 +95,19 @@ namespace Ryujinx.Ava.UI.ViewModels.Input public bool IsModified { get; set; } public event Action NotifyChangesEvent; + public object ConfigViewModel + { + get => _configViewModel; + set + { + _configViewModel = value; + + VisualStick.UpdateConfig(value); + + OnPropertyChanged(); + } + } + public PlayerIndex PlayerIdChoose { get => _playerIdChoose; @@ -269,6 +283,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input Devices = []; ProfilesList = []; DeviceList = []; + VisualStick = new StickVisualizer(this); ControllerImage = ProControllerResource; @@ -289,12 +304,12 @@ namespace Ryujinx.Ava.UI.ViewModels.Input if (Config is StandardKeyboardInputConfig keyboardInputConfig) { - ConfigViewModel = new KeyboardInputViewModel(this, new KeyboardInputConfig(keyboardInputConfig)); + ConfigViewModel = new KeyboardInputViewModel(this, new KeyboardInputConfig(keyboardInputConfig), VisualStick); } if (Config is StandardControllerInputConfig controllerInputConfig) { - ConfigViewModel = new ControllerInputViewModel(this, new GamepadInputConfig(controllerInputConfig)); + ConfigViewModel = new ControllerInputViewModel(this, new GamepadInputConfig(controllerInputConfig), VisualStick); } } @@ -897,6 +912,8 @@ namespace Ryujinx.Ava.UI.ViewModels.Input _mainWindow.ViewModel.AppHost?.NpadManager.UnblockInputUpdates(); + VisualStick.Dispose(); + SelectedGamepad?.Dispose(); AvaloniaKeyboardDriver.Dispose(); diff --git a/src/Ryujinx/UI/ViewModels/Input/KeyboardInputViewModel.cs b/src/Ryujinx/UI/ViewModels/Input/KeyboardInputViewModel.cs index 5ff9bb578..bab8db7ce 100644 --- a/src/Ryujinx/UI/ViewModels/Input/KeyboardInputViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/Input/KeyboardInputViewModel.cs @@ -6,7 +6,29 @@ namespace Ryujinx.Ava.UI.ViewModels.Input { public partial class KeyboardInputViewModel : BaseModel { - [ObservableProperty] private KeyboardInputConfig _config; + private KeyboardInputConfig _config; + public KeyboardInputConfig Config + { + get => _config; + set + { + _config = value; + + OnPropertyChanged(); + } + } + + private StickVisualizer _visualizer; + public StickVisualizer Visualizer + { + get => _visualizer; + set + { + _visualizer = value; + + OnPropertyChanged(); + } + } private bool _isLeft; public bool IsLeft @@ -38,9 +60,10 @@ namespace Ryujinx.Ava.UI.ViewModels.Input public readonly InputViewModel ParentModel; - public KeyboardInputViewModel(InputViewModel model, KeyboardInputConfig config) + public KeyboardInputViewModel(InputViewModel model, KeyboardInputConfig config, StickVisualizer visualizer) { ParentModel = model; + Visualizer = visualizer; model.NotifyChangesEvent += OnParentModelChanged; OnParentModelChanged(); Config = config; diff --git a/src/Ryujinx/UI/ViewModels/Input/LedInputViewModel.cs b/src/Ryujinx/UI/ViewModels/Input/LedInputViewModel.cs index 521b13859..effd07904 100644 --- a/src/Ryujinx/UI/ViewModels/Input/LedInputViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/Input/LedInputViewModel.cs @@ -3,7 +3,7 @@ using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using Humanizer; using Ryujinx.Ava.UI.Helpers; -using Ryujinx.Ava.Utilities.Configuration; +using Ryujinx.Ava.Systems.Configuration; using System.Globalization; namespace Ryujinx.Ava.UI.ViewModels.Input diff --git a/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs b/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs index 02b86bd24..21d0d9254 100644 --- a/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs @@ -3,7 +3,6 @@ using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Input; using Avalonia.Media; -using Avalonia.Media.Imaging; using Avalonia.Platform.Storage; using Avalonia.Threading; using CommunityToolkit.Mvvm.ComponentModel; @@ -17,14 +16,15 @@ using LibHac.Ns; using Ryujinx.Ava.Common; using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Input; +using Ryujinx.Ava.Systems; using Ryujinx.Ava.UI.Controls; using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Models; using Ryujinx.Ava.UI.Models.Generic; using Ryujinx.Ava.UI.Renderer; using Ryujinx.Ava.UI.Windows; -using Ryujinx.Ava.Utilities.AppLibrary; -using Ryujinx.Ava.Utilities.Configuration; +using Ryujinx.Ava.Systems.AppLibrary; +using Ryujinx.Ava.Systems.Configuration; using Ryujinx.Common; using Ryujinx.Common.Configuration; using Ryujinx.Common.Helper; @@ -46,7 +46,6 @@ using System.Collections.ObjectModel; using System.Globalization; using System.IO; using System.Linq; -using System.Reflection; using System.Threading; using System.Threading.Tasks; using Key = Ryujinx.Input.Key; @@ -137,12 +136,6 @@ namespace Ryujinx.Ava.UI.ViewModels // Key is Title ID public SafeDictionary LdnData = []; - // The UI specifically uses a thicker bordered variant of the icon to avoid crunching out the border at lower resolutions. - // For an example of this, download canary 1.2.95, then open the settings menu, and look at the icon in the top-left. - // The border gets reduced to colored pixels in the 4 corners. - public static readonly Bitmap IconBitmap = - new(Assembly.GetAssembly(typeof(MainWindowViewModel))!.GetManifestResourceStream("Ryujinx.Assets.UIImages.Logo_Ryujinx_AntiAlias.png")!); - public MainWindow Window { get; init; } internal AppHost AppHost { get; set; } @@ -355,6 +348,11 @@ namespace Ryujinx.Ava.UI.ViewModels _ => null, }; } + set + { + ListSelectedApplication = value; + GridSelectedApplication = value; + } } public bool HasCompatibilityEntry => SelectedApplication.HasPlayabilityInfo; @@ -1084,7 +1082,7 @@ namespace Ryujinx.Ava.UI.ViewModels _rendererWaitEvent.WaitOne(); AppHost?.Start(); - + AppHost?.DisposeContext(); } @@ -1550,8 +1548,50 @@ namespace Ryujinx.Ava.UI.ViewModels } } + public bool InitializeUserConfig(ApplicationData application) + { + // Code where conditions will be met before loading the user configuration (Global Config) + BackendThreading backendThreadingValue = ConfigurationState.Instance.Graphics.BackendThreading.Value; + string BackendThreadingInit = Program.BackendThreadingArg; + + if (BackendThreadingInit is null) + { + BackendThreadingInit = ConfigurationState.Instance.Graphics.BackendThreading.Value.ToString(); + } + + // If a configuration is found in the "/games/xxxxxxxxxxxxxx" folder, the program will load the user setting. + string idGame = application.IdBaseString; + if (ConfigurationFileFormat.TryLoad(Program.GetDirGameUserConfig(idGame), out ConfigurationFileFormat configurationFileFormat)) + { + // Loads the user configuration, having previously changed the global configuration to the user configuration + ConfigurationState.Instance.Load(configurationFileFormat, Program.GetDirGameUserConfig(idGame, true, true), idGame); + } + + // Code where conditions will be executed after loading user configuration + if (ConfigurationState.Instance.Graphics.BackendThreading.Value.ToString() != BackendThreadingInit) + { + + List Arguments = new List + { + "--bt", ConfigurationState.Instance.Graphics.BackendThreading.Value.ToString() // BackendThreading + }; + + Rebooter.RebootAppWithGame(application.Path, Arguments); + + return true; + } + + return false; + } + public async Task LoadApplication(ApplicationData application, bool startFullscreen = false, BlitStruct? customNacpData = null) { + + if (InitializeUserConfig(application)) + { + return; + } + if (AppHost != null) { await ContentDialogHelper.CreateInfoDialog( @@ -1567,14 +1607,14 @@ namespace Ryujinx.Ava.UI.ViewModels #if RELEASE await PerformanceCheck(); #endif - + Logger.RestartTime(); SelectedIcon ??= ApplicationLibrary.GetApplicationIcon(application.Path, ConfigurationState.Instance.System.Language, application.Id); PrepareLoadScreen(); - RendererHostControl = new RendererHost(application.Id.ToString("X16")); + RendererHostControl = new RendererHost(); AppHost = new AppHost( RendererHostControl, @@ -1614,6 +1654,7 @@ namespace Ryujinx.Ava.UI.ViewModels Thread gameThread = new(InitializeGame) { Name = "GUI.WindowThread" }; gameThread.Start(); + } public void SwitchToRenderer(bool startFullscreen) => @@ -1700,7 +1741,7 @@ namespace Ryujinx.Ava.UI.ViewModels string titleId = AppHost.Device.Processes.ActiveApplication.ProgramIdText.ToUpper(); AmiiboWindow window = new(ShowAll, LastScannedAmiiboId, titleId); - await window.ShowDialog(Window); + await StyleableAppWindow.ShowAsync(window); if (window.IsScanned) { @@ -1747,7 +1788,7 @@ namespace Ryujinx.Ava.UI.ViewModels if (WindowState is not WindowState.Normal) { WindowState = WindowState.Normal; - Window.TitleBar.ExtendsContentIntoTitleBar = !ConfigurationState.Instance.ShowTitleBar; + Window.TitleBar.ExtendsContentIntoTitleBar = !ConfigurationState.Instance.ShowOldUI; if (IsGameRunning) { diff --git a/src/Ryujinx/UI/ViewModels/ModManagerViewModel.cs b/src/Ryujinx/UI/ViewModels/ModManagerViewModel.cs index cda7e34cf..081ca0912 100644 --- a/src/Ryujinx/UI/ViewModels/ModManagerViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/ModManagerViewModel.cs @@ -7,7 +7,7 @@ using Gommon; using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Models; -using Ryujinx.Ava.Utilities.AppLibrary; +using Ryujinx.Ava.Systems.AppLibrary; using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; using Ryujinx.Common.Utilities; diff --git a/src/Ryujinx/UI/ViewModels/SettingsHacksViewModel.cs b/src/Ryujinx/UI/ViewModels/SettingsHacksViewModel.cs index 5096a716d..022b7481e 100644 --- a/src/Ryujinx/UI/ViewModels/SettingsHacksViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/SettingsHacksViewModel.cs @@ -1,6 +1,6 @@ using CommunityToolkit.Mvvm.ComponentModel; using Gommon; -using Ryujinx.Ava.Utilities.Configuration; +using Ryujinx.Ava.Systems.Configuration; namespace Ryujinx.Ava.UI.ViewModels { @@ -16,26 +16,12 @@ namespace Ryujinx.Ava.UI.ViewModels } [ObservableProperty] private bool _xc2MenuSoftlockFix = ConfigurationState.Instance.Hacks.Xc2MenuSoftlockFix; - [ObservableProperty] private bool _shaderTranslationDelayEnabled = ConfigurationState.Instance.Hacks.EnableShaderTranslationDelay; - private int _shaderTranslationSleepDelay = ConfigurationState.Instance.Hacks.ShaderTranslationDelay; - - public string ShaderTranslationDelayValueText => $"{ShaderTranslationDelay}ms"; - - public int ShaderTranslationDelay - { - get => _shaderTranslationSleepDelay; - set - { - _shaderTranslationSleepDelay = value; - - OnPropertiesChanged(nameof(ShaderTranslationDelay), nameof(ShaderTranslationDelayValueText)); - } - } + [ObservableProperty] private bool _nifmDisableIsAnyInternetRequestAccepted = ConfigurationState.Instance.Hacks.DisableNifmIsAnyInternetRequestAccepted; public static string Xc2MenuFixTooltip { get; } = Lambda.String(sb => { sb.AppendLine( - "This fix applies a 2ms delay (via 'Thread.Sleep(2)') every time the game tries to read data from the emulated Switch filesystem.") + "This hack applies a 2ms delay (via 'Thread.Sleep(2)') every time the game tries to read data from the emulated Switch filesystem.") .AppendLine(); sb.AppendLine("From the issue on GitHub:").AppendLine(); @@ -44,13 +30,14 @@ namespace Ryujinx.Ava.UI.ViewModels "there is a low chance that the game will softlock, " + "the submenu won't show up, while background music is still there."); }); - - public static string ShaderTranslationDelayTooltip { get; } = Lambda.String(sb => + + public static string NifmDisableIsAnyInternetRequestAcceptedTooltip { get; } = Lambda.String(sb => { - sb.AppendLine("This hack applies the delay you specify every time shaders are attempted to be translated.") + sb.AppendLine( + "This hack simply sets 'IsAnyInternetRequestAccepted' to 'false' when initializing the Nifm IGeneralService.") .AppendLine(); - sb.Append("Configurable via slider, only when this option is enabled."); + sb.Append("Lets DOOM 2016 go in game."); }); } } diff --git a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs index 40afad3b6..866a7f3e1 100644 --- a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs @@ -1,5 +1,6 @@ using Avalonia.Collections; using Avalonia.Controls; +using Avalonia.Media.Imaging; using Avalonia.Threading; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; @@ -11,9 +12,9 @@ using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Models.Input; using Ryujinx.Ava.UI.Windows; -using Ryujinx.Ava.Utilities.Configuration; -using Ryujinx.Ava.Utilities.Configuration.System; -using Ryujinx.Ava.Utilities.Configuration.UI; +using Ryujinx.Ava.Systems.Configuration; +using Ryujinx.Ava.Systems.Configuration.System; +using Ryujinx.Ava.Systems.Configuration.UI; using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration.Multiplayer; using Ryujinx.Common.GraphicsDriver; @@ -27,6 +28,7 @@ using Ryujinx.HLE.HOS.Services.Time.TimeZone; using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.IO; using System.Linq; using System.Net.NetworkInformation; using System.Threading.Tasks; @@ -68,6 +70,19 @@ namespace Ryujinx.Ava.UI.ViewModels public SettingsHacksViewModel DirtyHacks { get; } + private readonly bool _isGameRunning; + private Bitmap _gameIcon; + private string _gameTitle; + private string _gamePath; + private string _gameId; + public bool IsGameRunning => _isGameRunning; + public Bitmap GameIcon => _gameIcon; + public string GamePath => _gamePath; + public string GameTitle => _gameTitle; + public string GameId => _gameId; + public bool IsGameTitleNotNull => !string.IsNullOrEmpty(GameTitle); + public double PanelOpacity => IsGameTitleNotNull ? 0.5 : 1; + public int ResolutionScale { get => _resolutionScale; @@ -115,11 +130,10 @@ namespace Ryujinx.Ava.UI.ViewModels public bool IsOpenGLAvailable => !OperatingSystem.IsMacOS(); public bool EnableDiscordIntegration { get; set; } - public bool CheckUpdatesOnStart { get; set; } public bool ShowConfirmExit { get; set; } public bool IgnoreApplet { get; set; } public bool RememberWindowState { get; set; } - public bool ShowTitleBar { get; set; } + public bool ShowOldUI { get; set; } public int HideCursor { get; set; } public int UpdateCheckerType { get; set; } public bool EnableDockedMode { get; set; } @@ -341,7 +355,7 @@ namespace Ryujinx.Ava.UI.ViewModels public bool IsInvalidLdnPassphraseVisible { get; set; } - public SettingsViewModel(VirtualFileSystem virtualFileSystem, ContentManager contentManager) : this() + public SettingsViewModel(VirtualFileSystem virtualFileSystem, ContentManager contentManager) : this(false) { _virtualFileSystem = virtualFileSystem; _contentManager = contentManager; @@ -354,7 +368,51 @@ namespace Ryujinx.Ava.UI.ViewModels } } - public SettingsViewModel() + public SettingsViewModel( + VirtualFileSystem virtualFileSystem, + ContentManager contentManager, + bool gameRunning, + string gamePath, + string gameName, + string gameId, + byte[] gameIconData, + bool enableToLoadCustomConfig) : this(enableToLoadCustomConfig) + { + _virtualFileSystem = virtualFileSystem; + _contentManager = contentManager; + + if (gameIconData != null && gameIconData.Length > 0) + { + using (var ms = new MemoryStream(gameIconData)) + { + _gameIcon = new Bitmap(ms); + } + } + + _isGameRunning = gameRunning; + _gamePath = gamePath; + _gameTitle = gameName; + _gameId = gameId; + + if (enableToLoadCustomConfig) // During the game. If there is no user config, then load the global config window + { + string gameDir = Program.GetDirGameUserConfig(gameId, false, true); + if (ConfigurationFileFormat.TryLoad(gameDir, out ConfigurationFileFormat configurationFileFormat)) + { + ConfigurationState.Instance.Load(configurationFileFormat, gameDir, gameId); + } + + LoadCurrentConfiguration(); // Needed to load custom configuration + } + + if (Program.PreviewerDetached) + { + Task.Run(LoadTimeZones); + + } + } + + public SettingsViewModel(bool noLoadGlobalConfig = false) { GameDirectories = []; AutoloadDirectories = []; @@ -369,7 +427,9 @@ namespace Ryujinx.Ava.UI.ViewModels if (Program.PreviewerDetached) { Task.Run(LoadAvailableGpus); - LoadCurrentConfiguration(); + + // if (!noLoadGlobalConfig)// Default is false, but loading custom config avoids double call + LoadCurrentConfiguration(); DirtyHacks = new SettingsHacksViewModel(this); } @@ -482,10 +542,9 @@ namespace Ryujinx.Ava.UI.ViewModels // User Interface EnableDiscordIntegration = config.EnableDiscordIntegration; - CheckUpdatesOnStart = config.CheckUpdatesOnStart; ShowConfirmExit = config.ShowConfirmExit; RememberWindowState = config.RememberWindowState; - ShowTitleBar = config.ShowTitleBar; + ShowOldUI = config.ShowOldUI; HideCursor = (int)config.HideCursor.Value; UpdateCheckerType = (int)config.UpdateCheckerType.Value; FocusLostActionType = (int)config.FocusLostActionType.Value; @@ -599,15 +658,14 @@ namespace Ryujinx.Ava.UI.ViewModels // User Interface config.EnableDiscordIntegration.Value = EnableDiscordIntegration; - config.CheckUpdatesOnStart.Value = CheckUpdatesOnStart; config.ShowConfirmExit.Value = ShowConfirmExit; config.RememberWindowState.Value = RememberWindowState; - config.ShowTitleBar.Value = ShowTitleBar; + config.ShowOldUI.Value = ShowOldUI; config.HideCursor.Value = (HideCursorMode)HideCursor; config.UpdateCheckerType.Value = (UpdaterType)UpdateCheckerType; config.FocusLostActionType.Value = (FocusLostType)FocusLostActionType; - config.UI.GameDirs.Value = [..GameDirectories]; - config.UI.AutoloadDirs.Value = [..AutoloadDirectories]; + config.UI.GameDirs.Value = [.. GameDirectories]; + config.UI.AutoloadDirs.Value = [.. AutoloadDirectories]; config.UI.BaseStyle.Value = BaseStyleIndex switch { @@ -628,10 +686,10 @@ namespace Ryujinx.Ava.UI.ViewModels // System config.System.Region.Value = (Region)Region; - + if (config.System.Language.Value != (Language)Language) GameListNeedsRefresh = true; - + config.System.Language.Value = (Language)Language; if (_validTzRegions.Contains(TimeZone)) { @@ -718,11 +776,11 @@ namespace Ryujinx.Ava.UI.ViewModels config.Multiplayer.DisableP2p.Value = DisableP2P; config.Multiplayer.LdnPassphrase.Value = LdnPassphrase; config.Multiplayer.LdnServer.Value = LdnServer; - + // Dirty Hacks config.Hacks.Xc2MenuSoftlockFix.Value = DirtyHacks.Xc2MenuSoftlockFix; - config.Hacks.EnableShaderTranslationDelay.Value = DirtyHacks.ShaderTranslationDelayEnabled; - config.Hacks.ShaderTranslationDelay.Value = DirtyHacks.ShaderTranslationDelay; + config.Hacks.DisableNifmIsAnyInternetRequestAccepted.Value = + DirtyHacks.NifmDisableIsAnyInternetRequestAccepted; config.ToFileFormat().SaveConfig(Program.ConfigurationPath); @@ -736,7 +794,11 @@ namespace Ryujinx.Ava.UI.ViewModels private static void RevertIfNotSaved() { - Program.ReloadConfig(); + // maybe this is an unnecessary check(all options need to be tested) + if (string.IsNullOrEmpty(Program.GlobalConfigurationPath)) + { + Program.ReloadConfig(); + } } public void ApplyButton() @@ -744,6 +806,26 @@ namespace Ryujinx.Ava.UI.ViewModels SaveSettings(); } + public void DeleteConfigGame() + { + string gameDir = Program.GetDirGameUserConfig(GameId,false,false); + + if (File.Exists(gameDir)) + { + File.Delete(gameDir); + } + + RevertIfNotSaved(); + CloseWindow?.Invoke(); + } + + public void SaveUserConfig() + { + SaveSettings(); + RevertIfNotSaved(); // Revert global configuration after saving user configuration + CloseWindow?.Invoke(); + } + public void OkButton() { SaveSettings(); diff --git a/src/Ryujinx/UI/ViewModels/TitleUpdateViewModel.cs b/src/Ryujinx/UI/ViewModels/TitleUpdateViewModel.cs index 2b88aceed..7ecfc1c87 100644 --- a/src/Ryujinx/UI/ViewModels/TitleUpdateViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/TitleUpdateViewModel.cs @@ -6,7 +6,7 @@ using FluentAvalonia.UI.Controls; using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Models; using Ryujinx.Ava.UI.Helpers; -using Ryujinx.Ava.Utilities.AppLibrary; +using Ryujinx.Ava.Systems.AppLibrary; using System.Collections.Generic; using System.IO; using System.Linq; diff --git a/src/Ryujinx/UI/ViewModels/UserFirmwareAvatarSelectorViewModel.cs b/src/Ryujinx/UI/ViewModels/UserFirmwareAvatarSelectorViewModel.cs index cc5b35dc6..6b4d7795d 100644 --- a/src/Ryujinx/UI/ViewModels/UserFirmwareAvatarSelectorViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/UserFirmwareAvatarSelectorViewModel.cs @@ -21,7 +21,7 @@ using Image = SkiaSharp.SKImage; namespace Ryujinx.Ava.UI.ViewModels { - internal partial class UserFirmwareAvatarSelectorViewModel : BaseModel + public partial class UserFirmwareAvatarSelectorViewModel : BaseModel { private static readonly Dictionary _avatarStore = new(); diff --git a/src/Ryujinx/UI/ViewModels/UserProfileImageSelectorViewModel.cs b/src/Ryujinx/UI/ViewModels/UserProfileImageSelectorViewModel.cs index 36a9a62f9..f9f9ca2f5 100644 --- a/src/Ryujinx/UI/ViewModels/UserProfileImageSelectorViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/UserProfileImageSelectorViewModel.cs @@ -2,7 +2,7 @@ using CommunityToolkit.Mvvm.ComponentModel; namespace Ryujinx.Ava.UI.ViewModels { - internal partial class UserProfileImageSelectorViewModel : BaseModel + public partial class UserProfileImageSelectorViewModel : BaseModel { [ObservableProperty] private bool _firmwareFound; } diff --git a/src/Ryujinx/UI/ViewModels/XCITrimmerViewModel.cs b/src/Ryujinx/UI/ViewModels/XciTrimmerViewModel.cs similarity index 98% rename from src/Ryujinx/UI/ViewModels/XCITrimmerViewModel.cs rename to src/Ryujinx/UI/ViewModels/XciTrimmerViewModel.cs index 560f852db..2085ffe26 100644 --- a/src/Ryujinx/UI/ViewModels/XCITrimmerViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/XciTrimmerViewModel.cs @@ -6,7 +6,7 @@ using Ryujinx.Ava.Common; using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Models; using Ryujinx.Ava.UI.Helpers; -using Ryujinx.Ava.Utilities.AppLibrary; +using Ryujinx.Ava.Systems.AppLibrary; using Ryujinx.Common.Utilities; using System.Collections.Generic; using System.Collections.ObjectModel; @@ -16,7 +16,7 @@ using static Ryujinx.Common.Utilities.XCIFileTrimmer; namespace Ryujinx.Ava.UI.ViewModels { - public class XCITrimmerViewModel : BaseModel + public class XciTrimmerViewModel : BaseModel { private const long _bytesPerMB = 1024 * 1024; private enum ProcessingMode @@ -46,7 +46,7 @@ namespace Ryujinx.Ava.UI.ViewModels private SortField _sortField = SortField.Name; private bool _sortAscending = true; - public XCITrimmerViewModel(MainWindowViewModel mainWindowViewModel) + public XciTrimmerViewModel(MainWindowViewModel mainWindowViewModel) { _logger = new XCITrimmerLog.TrimmerWindow(this); _mainWindowViewModel = mainWindowViewModel; @@ -254,9 +254,9 @@ namespace Ryujinx.Ava.UI.ViewModels private class CompareXCITrimmerFiles : IComparer { - private XCITrimmerViewModel _viewModel; + private XciTrimmerViewModel _viewModel; - public CompareXCITrimmerFiles(XCITrimmerViewModel ViewModel) + public CompareXCITrimmerFiles(XciTrimmerViewModel ViewModel) { _viewModel = ViewModel; } diff --git a/src/Ryujinx/UI/Windows/AboutWindow.axaml b/src/Ryujinx/UI/Views/Dialog/AboutView.axaml similarity index 99% rename from src/Ryujinx/UI/Windows/AboutWindow.axaml rename to src/Ryujinx/UI/Views/Dialog/AboutView.axaml index e215cf27e..4c3da21e4 100644 --- a/src/Ryujinx/UI/Windows/AboutWindow.axaml +++ b/src/Ryujinx/UI/Views/Dialog/AboutView.axaml @@ -1,5 +1,5 @@ { - public AboutWindow() + public AboutView() { InitializeComponent(); @@ -33,19 +32,10 @@ namespace Ryujinx.Ava.UI.Windows PrimaryButtonText = string.Empty, SecondaryButtonText = string.Empty, CloseButtonText = LocaleManager.Instance[LocaleKeys.UserProfilesClose], - Content = new AboutWindow { DataContext = viewModel } + Content = new AboutView { ViewModel = viewModel } }; - Style closeButton = new(x => x.Name("CloseButton")); - closeButton.Setters.Add(new Setter(WidthProperty, 80d)); - - Style closeButtonParent = new(x => x.Name("CommandSpace")); - closeButtonParent.Setters.Add(new Setter(HorizontalAlignmentProperty, HorizontalAlignment.Right)); - - contentDialog.Styles.Add(closeButton); - contentDialog.Styles.Add(closeButtonParent); - - await ContentDialogHelper.ShowAsync(contentDialog); + await ContentDialogHelper.ShowAsync(contentDialog.ApplyStyles()); } private void Button_OnClick(object sender, RoutedEventArgs e) diff --git a/src/Ryujinx/UI/Controls/ApplicationDataView.axaml b/src/Ryujinx/UI/Views/Dialog/ApplicationDataView.axaml similarity index 99% rename from src/Ryujinx/UI/Controls/ApplicationDataView.axaml rename to src/Ryujinx/UI/Views/Dialog/ApplicationDataView.axaml index 92e4d1ac3..7ba4ad784 100644 --- a/src/Ryujinx/UI/Controls/ApplicationDataView.axaml +++ b/src/Ryujinx/UI/Views/Dialog/ApplicationDataView.axaml @@ -7,7 +7,7 @@ xmlns:ui="using:FluentAvalonia.UI.Controls" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" - x:Class="Ryujinx.Ava.UI.Controls.ApplicationDataView" + x:Class="Ryujinx.Ava.UI.Views.Dialog.ApplicationDataView" x:DataType="viewModels:ApplicationDataViewModel"> { public static async Task Show(ApplicationData appData) { @@ -25,20 +25,10 @@ namespace Ryujinx.Ava.UI.Controls SecondaryButtonText = string.Empty, CloseButtonText = LocaleManager.Instance[LocaleKeys.SettingsButtonClose], MinWidth = 256, - Content = new ApplicationDataView { DataContext = new ApplicationDataViewModel(appData) } + Content = new ApplicationDataView { ViewModel = new ApplicationDataViewModel(appData) } }; - Style closeButton = new(x => x.Name("CloseButton")); - closeButton.Setters.Add(new Setter(WidthProperty, 160d)); - - Style closeButtonParent = new(x => x.Name("CommandSpace")); - closeButtonParent.Setters.Add(new Setter(HorizontalAlignmentProperty, - Avalonia.Layout.HorizontalAlignment.Center)); - - contentDialog.Styles.Add(closeButton); - contentDialog.Styles.Add(closeButtonParent); - - await ContentDialogHelper.ShowAsync(contentDialog); + await ContentDialogHelper.ShowAsync(contentDialog.ApplyStyles(160, HorizontalAlignment.Center)); } public ApplicationDataView() @@ -54,21 +44,18 @@ namespace Ryujinx.Ava.UI.Controls if (RyujinxApp.AppLifetime.Windows.TryGetFirst(x => x is ContentDialogOverlayWindow, out Window window)) window.Close(ContentDialogResult.None); - await CompatibilityList.Show((string)playabilityLabel.Tag); + await CompatibilityListWindow.Show((string)playabilityLabel.Tag); } private async void IdString_OnClick(object sender, RoutedEventArgs e) { - if (DataContext is not MainWindowViewModel mwvm) - return; - if (sender is not Button { Content: TextBlock idText }) return; if (!RyujinxApp.IsClipboardAvailable(out IClipboard clipboard)) return; - ApplicationData appData = mwvm.Applications.FirstOrDefault(it => it.IdString == idText.Text); + ApplicationData appData = RyujinxApp.MainWindow.ViewModel.Applications.FirstOrDefault(it => it.IdString == idText.Text); if (appData is null) return; diff --git a/src/Ryujinx/UI/Controls/DlcSelectView.axaml b/src/Ryujinx/UI/Views/Dialog/DlcSelectView.axaml similarity index 98% rename from src/Ryujinx/UI/Controls/DlcSelectView.axaml rename to src/Ryujinx/UI/Views/Dialog/DlcSelectView.axaml index 790c6dd3b..05d1f7ae5 100644 --- a/src/Ryujinx/UI/Controls/DlcSelectView.axaml +++ b/src/Ryujinx/UI/Views/Dialog/DlcSelectView.axaml @@ -7,7 +7,7 @@ xmlns:models="using:Ryujinx.Ava.Common.Models" xmlns:viewModels="using:Ryujinx.Ava.UI.ViewModels" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" - x:Class="Ryujinx.Ava.UI.Controls.DlcSelectView" + x:Class="Ryujinx.Ava.UI.Views.Dialog.DlcSelectView" x:DataType="viewModels:DlcSelectViewModel"> { public DlcSelectView() { @@ -28,20 +27,10 @@ namespace Ryujinx.Ava.UI.Controls PrimaryButtonText = LocaleManager.Instance[LocaleKeys.Continue], SecondaryButtonText = string.Empty, CloseButtonText = string.Empty, - Content = new DlcSelectView { DataContext = viewModel } + Content = new DlcSelectView { ViewModel = viewModel } }; - Style closeButton = new(x => x.Name("CloseButton")); - closeButton.Setters.Add(new Setter(WidthProperty, 80d)); - - Style closeButtonParent = new(x => x.Name("CommandSpace")); - closeButtonParent.Setters.Add(new Setter(HorizontalAlignmentProperty, - Avalonia.Layout.HorizontalAlignment.Right)); - - contentDialog.Styles.Add(closeButton); - contentDialog.Styles.Add(closeButtonParent); - - await ContentDialogHelper.ShowAsync(contentDialog); + await ContentDialogHelper.ShowAsync(contentDialog.ApplyStyles()); return viewModel.SelectedDlc; } diff --git a/src/Ryujinx/UI/Windows/DownloadableContentManagerWindow.axaml b/src/Ryujinx/UI/Views/Dialog/DownloadableContentManagerView.axaml similarity index 99% rename from src/Ryujinx/UI/Windows/DownloadableContentManagerWindow.axaml rename to src/Ryujinx/UI/Views/Dialog/DownloadableContentManagerView.axaml index e2c4fe16e..8b97a4822 100644 --- a/src/Ryujinx/UI/Windows/DownloadableContentManagerWindow.axaml +++ b/src/Ryujinx/UI/Views/Dialog/DownloadableContentManagerView.axaml @@ -1,5 +1,5 @@ { - public DownloadableContentManagerViewModel ViewModel; - - public DownloadableContentManagerWindow() + public DownloadableContentManagerView() { - DataContext = this; - - InitializeComponent(); - } - - public DownloadableContentManagerWindow(ApplicationLibrary applicationLibrary, ApplicationData applicationData) - { - DataContext = ViewModel = new DownloadableContentManagerViewModel(applicationLibrary, applicationData); - InitializeComponent(); } @@ -36,8 +26,11 @@ namespace Ryujinx.Ava.UI.Windows PrimaryButtonText = string.Empty, SecondaryButtonText = string.Empty, CloseButtonText = string.Empty, - Content = new DownloadableContentManagerWindow(applicationLibrary, applicationData), Title = string.Format(LocaleManager.Instance[LocaleKeys.DlcWindowTitle], applicationData.Name, applicationData.IdBaseString), + Content = new DownloadableContentManagerView + { + ViewModel = new DownloadableContentManagerViewModel(applicationLibrary, applicationData) + } }; Style bottomBorder = new(x => x.OfType().Name("DialogSpace").Child().OfType()); diff --git a/src/Ryujinx/UI/Windows/ModManagerWindow.axaml b/src/Ryujinx/UI/Views/Dialog/ModManagerView.axaml similarity index 95% rename from src/Ryujinx/UI/Windows/ModManagerWindow.axaml rename to src/Ryujinx/UI/Views/Dialog/ModManagerView.axaml index b50ea2c8e..ee913b56d 100644 --- a/src/Ryujinx/UI/Windows/ModManagerWindow.axaml +++ b/src/Ryujinx/UI/Views/Dialog/ModManagerView.axaml @@ -10,7 +10,7 @@ Width="500" Height="380" mc:Ignorable="d" - x:Class="Ryujinx.Ava.UI.Windows.ModManagerWindow" + x:Class="Ryujinx.Ava.UI.Views.Dialog.ModManagerView" x:CompileBindings="True" x:DataType="viewModels:ModManagerViewModel" Focusable="True"> @@ -70,11 +70,7 @@ - - - - - + { - public readonly ModManagerViewModel ViewModel; - - public ModManagerWindow() + public ModManagerView() { - DataContext = this; - - InitializeComponent(); - } - - public ModManagerWindow(ulong titleId, ulong titleIdBase, ApplicationLibrary applicationLibrary) - { - DataContext = ViewModel = new ModManagerViewModel(titleId, titleIdBase, applicationLibrary); - InitializeComponent(); } @@ -38,7 +28,10 @@ namespace Ryujinx.Ava.UI.Windows PrimaryButtonText = string.Empty, SecondaryButtonText = string.Empty, CloseButtonText = string.Empty, - Content = new ModManagerWindow(titleId, titleIdBase, appLibrary), + Content = new ModManagerView + { + ViewModel = new ModManagerViewModel(titleId, titleIdBase, appLibrary) + }, Title = string.Format(LocaleManager.Instance[LocaleKeys.ModWindowTitle], titleName, titleId.ToString("X16")), }; diff --git a/src/Ryujinx/UI/Windows/TitleUpdateWindow.axaml b/src/Ryujinx/UI/Views/Dialog/TitleUpdateManagerView.axaml similarity index 99% rename from src/Ryujinx/UI/Windows/TitleUpdateWindow.axaml rename to src/Ryujinx/UI/Views/Dialog/TitleUpdateManagerView.axaml index 4da727db5..ba3e85e8b 100644 --- a/src/Ryujinx/UI/Windows/TitleUpdateWindow.axaml +++ b/src/Ryujinx/UI/Views/Dialog/TitleUpdateManagerView.axaml @@ -1,5 +1,5 @@ { - public readonly TitleUpdateViewModel ViewModel; - - public TitleUpdateWindow() + public TitleUpdateManagerView() { - DataContext = this; - - InitializeComponent(); - } - - public TitleUpdateWindow(ApplicationLibrary applicationLibrary, ApplicationData applicationData) - { - DataContext = ViewModel = new TitleUpdateViewModel(applicationLibrary, applicationData); - InitializeComponent(); } @@ -36,8 +26,11 @@ namespace Ryujinx.Ava.UI.Windows PrimaryButtonText = string.Empty, SecondaryButtonText = string.Empty, CloseButtonText = string.Empty, - Content = new TitleUpdateWindow(applicationLibrary, applicationData), Title = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.GameUpdateWindowHeading, applicationData.Name, applicationData.IdBaseString), + Content = new TitleUpdateManagerView + { + ViewModel = new TitleUpdateViewModel(applicationLibrary, applicationData) + } }; Style bottomBorder = new(x => x.OfType().Name("DialogSpace").Child().OfType()); diff --git a/src/Ryujinx/UI/Windows/XCITrimmerWindow.axaml b/src/Ryujinx/UI/Views/Dialog/XciTrimmerView.axaml similarity index 88% rename from src/Ryujinx/UI/Windows/XCITrimmerWindow.axaml rename to src/Ryujinx/UI/Views/Dialog/XciTrimmerView.axaml index bc135d4ec..d149d246c 100644 --- a/src/Ryujinx/UI/Windows/XCITrimmerWindow.axaml +++ b/src/Ryujinx/UI/Views/Dialog/XciTrimmerView.axaml @@ -1,5 +1,5 @@ - - - - - - - - + @@ -30,12 +23,7 @@ Margin="0 0 10 10" IsVisible="{Binding !Processing}" Grid.Row="1"> - - - - - - + @@ -145,11 +133,7 @@ - - - - - + - - - - - + @@ -226,15 +206,7 @@ BorderThickness="1" CornerRadius="5" Padding="2.5"> - - - - - - - - - + - - - - - + { - public XCITrimmerViewModel ViewModel; - - public XCITrimmerWindow() + public XciTrimmerView() { - DataContext = this; - - InitializeComponent(); - } - - public XCITrimmerWindow(MainWindowViewModel mainWindowViewModel) - { - DataContext = ViewModel = new XCITrimmerViewModel(mainWindowViewModel); - InitializeComponent(); } @@ -35,7 +25,10 @@ namespace Ryujinx.Ava.UI.Windows PrimaryButtonText = string.Empty, SecondaryButtonText = string.Empty, CloseButtonText = string.Empty, - Content = new XCITrimmerWindow(RyujinxApp.MainWindow.ViewModel), + Content = new XciTrimmerView + { + ViewModel = new XciTrimmerViewModel(RyujinxApp.MainWindow.ViewModel) + }, Title = LocaleManager.Instance[LocaleKeys.XCITrimmerWindowTitle] }; @@ -70,7 +63,7 @@ namespace Ryujinx.Ava.UI.Windows public void Sort_Checked(object sender, RoutedEventArgs args) { if (sender is RadioButton { Tag: string sortField }) - ViewModel.SortingField = Enum.Parse(sortField); + ViewModel.SortingField = Enum.Parse(sortField); } public void Order_Checked(object sender, RoutedEventArgs args) diff --git a/src/Ryujinx/UI/Views/Input/ControllerInputView.axaml b/src/Ryujinx/UI/Views/Input/ControllerInputView.axaml index 0e0a7edb3..da8710850 100644 --- a/src/Ryujinx/UI/Views/Input/ControllerInputView.axaml +++ b/src/Ryujinx/UI/Views/Input/ControllerInputView.axaml @@ -4,10 +4,10 @@ xmlns:ext="clr-namespace:Ryujinx.Ava.Common.Markup" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" xmlns:controls="clr-namespace:Ryujinx.Ava.UI.Controls" xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels.Input" xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers" + xmlns:pt="using:Projektanker.Icons.Avalonia" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" d:DesignHeight="800" @@ -34,12 +34,7 @@ - - - - - + MinHeight="450" ColumnDefinitions="Auto,*,Auto"> - - - - - - - - + HorizontalAlignment="Stretch" ColumnDefinitions="*,*" RowDefinitions="*,*"> - + + + + + + + + + + + + + + + + + + + + + + + + + @@ -360,8 +429,8 @@ Minimum="0" Value="{Binding Config.TriggerThreshold, Mode=TwoWay}" /> + Width="25" + Text="{Binding Config.TriggerThreshold, StringFormat=\{0:0.00\}}" /> @@ -453,77 +522,49 @@ CornerRadius="5" VerticalAlignment="Bottom" HorizontalAlignment="Stretch"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + @@ -541,15 +582,7 @@ CornerRadius="5"> - - - - - - - - + HorizontalAlignment="Stretch" ColumnDefinitions="*,*" RowDefinitions="*,*"> { private ButtonKeyAssigner _currentAssigner; @@ -223,20 +224,12 @@ namespace Ryujinx.Ava.UI.Views.Input PointerPressed -= MouseClick; } - private IButtonAssigner CreateButtonAssigner(bool forStick) - { - IButtonAssigner assigner; - - ControllerInputViewModel controllerInputViewModel = DataContext as ControllerInputViewModel; - - assigner = new GamepadButtonAssigner( - controllerInputViewModel.ParentModel.SelectedGamepad, - (controllerInputViewModel.ParentModel.Config as StandardControllerInputConfig).TriggerThreshold, + private IButtonAssigner CreateButtonAssigner(bool forStick) => + new GamepadButtonAssigner( + ViewModel.ParentModel.SelectedGamepad, + (ViewModel.ParentModel.Config as StandardControllerInputConfig).TriggerThreshold, forStick); - return assigner; - } - protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) { base.OnDetachedFromVisualTree(e); diff --git a/src/Ryujinx/UI/Views/Input/InputView.axaml b/src/Ryujinx/UI/Views/Input/InputView.axaml index b5bfa666d..0dc8d9ab0 100644 --- a/src/Ryujinx/UI/Views/Input/InputView.axaml +++ b/src/Ryujinx/UI/Views/Input/InputView.axaml @@ -35,22 +35,13 @@ Margin="0 0 0 5" Orientation="Vertical" Spacing="5"> - - - - - - + - - - - + VerticalAlignment="Center" ColumnDefinitions="Auto,*"> - - - - - - - + VerticalAlignment="Center" ColumnDefinitions="Auto,*,Auto,Auto,Auto"> - - - - - - + - - - - - + HorizontalAlignment="Stretch" ColumnDefinitions="Auto,*,Auto"> - - - - + VerticalAlignment="Center" ColumnDefinitions="Auto,*"> { private bool _dialogOpen; - private InputViewModel ViewModel { get; set; } public InputView() { - DataContext = ViewModel = new InputViewModel(this); + ViewModel = new InputViewModel(this); InitializeComponent(); } diff --git a/src/Ryujinx/UI/Views/Input/KeyboardInputView.axaml b/src/Ryujinx/UI/Views/Input/KeyboardInputView.axaml index ca0a2fc41..fba290428 100644 --- a/src/Ryujinx/UI/Views/Input/KeyboardInputView.axaml +++ b/src/Ryujinx/UI/Views/Input/KeyboardInputView.axaml @@ -32,12 +32,7 @@ - - - - - + MinHeight="450" ColumnDefinitions="Auto,*,Auto"> - - - - - - - - + HorizontalAlignment="Stretch" ColumnDefinitions="*,*" RowDefinitions="*,*"> - + MinHeight="90"> + + + + + + + + + + + + + + + + + + + - - - - - - - - + HorizontalAlignment="Stretch" ColumnDefinitions="*,*" RowDefinitions="*,*"> { private ButtonKeyAssigner _currentAssigner; @@ -60,112 +61,109 @@ namespace Ryujinx.Ava.UI.Views.Input PointerPressed += MouseClick; - if (DataContext is not KeyboardInputViewModel viewModel) - return; - IKeyboard keyboard = - (IKeyboard)viewModel.ParentModel.AvaloniaKeyboardDriver.GetGamepad("0"); // Open Avalonia keyboard for cancel operations. + (IKeyboard)ViewModel.ParentModel.AvaloniaKeyboardDriver.GetGamepad("0"); // Open Avalonia keyboard for cancel operations. IButtonAssigner assigner = - new KeyboardKeyAssigner((IKeyboard)viewModel.ParentModel.SelectedGamepad); + new KeyboardKeyAssigner((IKeyboard)ViewModel.ParentModel.SelectedGamepad); - _currentAssigner.ButtonAssigned += (_, e) => + _currentAssigner.ButtonAssigned += (_, be) => { - if (e.ButtonValue.HasValue) + if (be.ButtonValue.HasValue) { - Button buttonValue = e.ButtonValue.Value; - viewModel.ParentModel.IsModified = true; + Button buttonValue = be.ButtonValue.Value; + ViewModel.ParentModel.IsModified = true; switch (button.Name) { case "ButtonZl": - viewModel.Config.ButtonZl = buttonValue.AsHidType(); + ViewModel.Config.ButtonZl = buttonValue.AsHidType(); break; case "ButtonL": - viewModel.Config.ButtonL = buttonValue.AsHidType(); + ViewModel.Config.ButtonL = buttonValue.AsHidType(); break; case "ButtonMinus": - viewModel.Config.ButtonMinus = buttonValue.AsHidType(); - break; + ViewModel.Config.ButtonMinus = buttonValue.AsHidType(); + break; case "LeftStickButton": - viewModel.Config.LeftStickButton = buttonValue.AsHidType(); + ViewModel.Config.LeftStickButton = buttonValue.AsHidType(); break; case "LeftStickUp": - viewModel.Config.LeftStickUp = buttonValue.AsHidType(); + ViewModel.Config.LeftStickUp = buttonValue.AsHidType(); break; case "LeftStickDown": - viewModel.Config.LeftStickDown = buttonValue.AsHidType(); + ViewModel.Config.LeftStickDown = buttonValue.AsHidType(); break; case "LeftStickRight": - viewModel.Config.LeftStickRight = buttonValue.AsHidType(); + ViewModel.Config.LeftStickRight = buttonValue.AsHidType(); break; case "LeftStickLeft": - viewModel.Config.LeftStickLeft = buttonValue.AsHidType(); + ViewModel.Config.LeftStickLeft = buttonValue.AsHidType(); break; case "DpadUp": - viewModel.Config.DpadUp = buttonValue.AsHidType(); + ViewModel.Config.DpadUp = buttonValue.AsHidType(); break; case "DpadDown": - viewModel.Config.DpadDown = buttonValue.AsHidType(); + ViewModel.Config.DpadDown = buttonValue.AsHidType(); break; case "DpadLeft": - viewModel.Config.DpadLeft = buttonValue.AsHidType(); + ViewModel.Config.DpadLeft = buttonValue.AsHidType(); break; case "DpadRight": - viewModel.Config.DpadRight = buttonValue.AsHidType(); + ViewModel.Config.DpadRight = buttonValue.AsHidType(); break; case "LeftButtonSr": - viewModel.Config.LeftButtonSr = buttonValue.AsHidType(); + ViewModel.Config.LeftButtonSr = buttonValue.AsHidType(); break; case "LeftButtonSl": - viewModel.Config.LeftButtonSl = buttonValue.AsHidType(); + ViewModel.Config.LeftButtonSl = buttonValue.AsHidType(); break; case "RightButtonSr": - viewModel.Config.RightButtonSr = buttonValue.AsHidType(); + ViewModel.Config.RightButtonSr = buttonValue.AsHidType(); break; case "RightButtonSl": - viewModel.Config.RightButtonSl = buttonValue.AsHidType(); + ViewModel.Config.RightButtonSl = buttonValue.AsHidType(); break; case "ButtonZr": - viewModel.Config.ButtonZr = buttonValue.AsHidType(); + ViewModel.Config.ButtonZr = buttonValue.AsHidType(); break; case "ButtonR": - viewModel.Config.ButtonR = buttonValue.AsHidType(); + ViewModel.Config.ButtonR = buttonValue.AsHidType(); break; case "ButtonPlus": - viewModel.Config.ButtonPlus = buttonValue.AsHidType(); + ViewModel.Config.ButtonPlus = buttonValue.AsHidType(); break; case "ButtonA": - viewModel.Config.ButtonA = buttonValue.AsHidType(); + ViewModel.Config.ButtonA = buttonValue.AsHidType(); break; case "ButtonB": - viewModel.Config.ButtonB = buttonValue.AsHidType(); + ViewModel.Config.ButtonB = buttonValue.AsHidType(); break; case "ButtonX": - viewModel.Config.ButtonX = buttonValue.AsHidType(); + ViewModel.Config.ButtonX = buttonValue.AsHidType(); break; case "ButtonY": - viewModel.Config.ButtonY = buttonValue.AsHidType(); + ViewModel.Config.ButtonY = buttonValue.AsHidType(); break; case "RightStickButton": - viewModel.Config.RightStickButton = buttonValue.AsHidType(); + ViewModel.Config.RightStickButton = buttonValue.AsHidType(); break; case "RightStickUp": - viewModel.Config.RightStickUp = buttonValue.AsHidType(); + ViewModel.Config.RightStickUp = buttonValue.AsHidType(); break; case "RightStickDown": - viewModel.Config.RightStickDown = buttonValue.AsHidType(); + ViewModel.Config.RightStickDown = buttonValue.AsHidType(); break; case "RightStickRight": - viewModel.Config.RightStickRight = buttonValue.AsHidType(); + ViewModel.Config.RightStickRight = buttonValue.AsHidType(); break; case "RightStickLeft": - viewModel.Config.RightStickLeft = buttonValue.AsHidType(); + ViewModel.Config.RightStickLeft = buttonValue.AsHidType(); break; case "ButtonCapture": - viewModel.Config.ButtonCapture = buttonValue.AsHidType(); + ViewModel.Config.ButtonCapture = buttonValue.AsHidType(); break; case "ButtonHome": - viewModel.Config.ButtonHome = buttonValue.AsHidType(); + ViewModel.Config.ButtonHome = buttonValue.AsHidType(); break; } } diff --git a/src/Ryujinx/UI/Views/Input/LedInputView.axaml.cs b/src/Ryujinx/UI/Views/Input/LedInputView.axaml.cs index ce28f6c1c..916ada37e 100644 --- a/src/Ryujinx/UI/Views/Input/LedInputView.axaml.cs +++ b/src/Ryujinx/UI/Views/Input/LedInputView.axaml.cs @@ -1,20 +1,18 @@ using Avalonia; -using Avalonia.Controls; using FluentAvalonia.UI.Controls; using Ryujinx.Ava.Common.Locale; +using Ryujinx.Ava.UI.Controls; using Ryujinx.Ava.UI.Models.Input; using Ryujinx.Ava.UI.ViewModels.Input; using System.Threading.Tasks; namespace Ryujinx.UI.Views.Input { - public partial class LedInputView : UserControl + public partial class LedInputView : RyujinxControl { - private readonly LedInputViewModel _viewModel; - public LedInputView(ControllerInputViewModel viewModel) { - DataContext = _viewModel = new LedInputViewModel + ViewModel = new LedInputViewModel { ParentModel = viewModel.ParentModel, TurnOffLed = viewModel.Config.TurnOffLed, @@ -29,20 +27,18 @@ namespace Ryujinx.UI.Views.Input private void ColorPickerButton_OnColorChanged(ColorPickerButton sender, ColorButtonColorChangedEventArgs args) { if (!args.NewColor.HasValue) return; - if (DataContext is not LedInputViewModel lvm) return; - if (!lvm.EnableLedChanging) return; - if (lvm.TurnOffLed) return; + if (!ViewModel.EnableLedChanging) return; + if (ViewModel.TurnOffLed) return; - lvm.ParentModel.SelectedGamepad.SetLed(args.NewColor.Value.ToUInt32()); + ViewModel.ParentModel.SelectedGamepad.SetLed(args.NewColor.Value.ToUInt32()); } private void ColorPickerButton_OnAttachedToVisualTree(object sender, VisualTreeAttachmentEventArgs e) { - if (DataContext is not LedInputViewModel lvm) return; - if (!lvm.EnableLedChanging) return; - if (lvm.TurnOffLed) return; + if (!ViewModel.EnableLedChanging) return; + if (ViewModel.TurnOffLed) return; - lvm.ParentModel.SelectedGamepad.SetLed(lvm.LedColor.ToUInt32()); + ViewModel.ParentModel.SelectedGamepad.SetLed(ViewModel.LedColor.ToUInt32()); } public static async Task Show(ControllerInputViewModel viewModel) @@ -57,13 +53,13 @@ namespace Ryujinx.UI.Views.Input CloseButtonText = LocaleManager.Instance[LocaleKeys.ControllerSettingsClose], Content = content, }; - contentDialog.PrimaryButtonClick += (sender, args) => + contentDialog.PrimaryButtonClick += (_, _) => { GamepadInputConfig config = viewModel.Config; - config.EnableLedChanging = content._viewModel.EnableLedChanging; - config.LedColor = content._viewModel.LedColor; - config.UseRainbowLed = content._viewModel.UseRainbowLed; - config.TurnOffLed = content._viewModel.TurnOffLed; + config.EnableLedChanging = content.ViewModel.EnableLedChanging; + config.LedColor = content.ViewModel.LedColor; + config.UseRainbowLed = content.ViewModel.UseRainbowLed; + config.TurnOffLed = content.ViewModel.TurnOffLed; }; await contentDialog.ShowAsync(); diff --git a/src/Ryujinx/UI/Views/Input/MotionInputView.axaml b/src/Ryujinx/UI/Views/Input/MotionInputView.axaml index 9096a06d1..abab04285 100644 --- a/src/Ryujinx/UI/Views/Input/MotionInputView.axaml +++ b/src/Ryujinx/UI/Views/Input/MotionInputView.axaml @@ -11,11 +11,7 @@ x:Class="Ryujinx.Ava.UI.Views.Input.MotionInputView" x:DataType="viewModels:MotionInputViewModel" Focusable="True"> - - - - - + - - - - - + - - - - - - - - - + { - private readonly MotionInputViewModel _viewModel; - public MotionInputView() { InitializeComponent(); @@ -20,7 +18,7 @@ namespace Ryujinx.Ava.UI.Views.Input { GamepadInputConfig config = viewModel.Config; - _viewModel = new MotionInputViewModel + ViewModel = new MotionInputViewModel { Slot = config.Slot, AltSlot = config.AltSlot, @@ -33,7 +31,6 @@ namespace Ryujinx.Ava.UI.Views.Input }; InitializeComponent(); - DataContext = _viewModel; } public static async Task Show(ControllerInputViewModel viewModel) @@ -48,17 +45,17 @@ namespace Ryujinx.Ava.UI.Views.Input CloseButtonText = LocaleManager.Instance[LocaleKeys.ControllerSettingsClose], Content = content, }; - contentDialog.PrimaryButtonClick += (sender, args) => + contentDialog.PrimaryButtonClick += (_, _) => { GamepadInputConfig config = viewModel.Config; - config.Slot = content._viewModel.Slot; - config.Sensitivity = content._viewModel.Sensitivity; - config.GyroDeadzone = content._viewModel.GyroDeadzone; - config.AltSlot = content._viewModel.AltSlot; - config.DsuServerHost = content._viewModel.DsuServerHost; - config.DsuServerPort = content._viewModel.DsuServerPort; - config.EnableCemuHookMotion = content._viewModel.EnableCemuHookMotion; - config.MirrorInput = content._viewModel.MirrorInput; + config.Slot = content.ViewModel.Slot; + config.Sensitivity = content.ViewModel.Sensitivity; + config.GyroDeadzone = content.ViewModel.GyroDeadzone; + config.AltSlot = content.ViewModel.AltSlot; + config.DsuServerHost = content.ViewModel.DsuServerHost; + config.DsuServerPort = content.ViewModel.DsuServerPort; + config.EnableCemuHookMotion = content.ViewModel.EnableCemuHookMotion; + config.MirrorInput = content.ViewModel.MirrorInput; }; await contentDialog.ShowAsync(); diff --git a/src/Ryujinx/UI/Views/Input/RumbleInputView.axaml b/src/Ryujinx/UI/Views/Input/RumbleInputView.axaml index 5f6cde5b5..98489aab0 100644 --- a/src/Ryujinx/UI/Views/Input/RumbleInputView.axaml +++ b/src/Ryujinx/UI/Views/Input/RumbleInputView.axaml @@ -10,11 +10,7 @@ x:Class="Ryujinx.Ava.UI.Views.Input.RumbleInputView" x:DataType="viewModels:RumbleInputViewModel" Focusable="True"> - - - - - + { - private readonly RumbleInputViewModel _viewModel; - public RumbleInputView() { InitializeComponent(); @@ -20,15 +18,13 @@ namespace Ryujinx.Ava.UI.Views.Input { GamepadInputConfig config = viewModel.Config; - _viewModel = new RumbleInputViewModel + ViewModel = new RumbleInputViewModel { StrongRumble = config.StrongRumble, WeakRumble = config.WeakRumble, }; InitializeComponent(); - - DataContext = _viewModel; } public static async Task Show(ControllerInputViewModel viewModel) @@ -44,11 +40,11 @@ namespace Ryujinx.Ava.UI.Views.Input Content = content, }; - contentDialog.PrimaryButtonClick += (sender, args) => + contentDialog.PrimaryButtonClick += (_, _) => { GamepadInputConfig config = viewModel.Config; - config.StrongRumble = content._viewModel.StrongRumble; - config.WeakRumble = content._viewModel.WeakRumble; + config.StrongRumble = content.ViewModel.StrongRumble; + config.WeakRumble = content.ViewModel.WeakRumble; }; await contentDialog.ShowAsync(); diff --git a/src/Ryujinx/UI/Views/Main/MainMenuBarView.axaml b/src/Ryujinx/UI/Views/Main/MainMenuBarView.axaml index 02836b35f..b08ba99e4 100644 --- a/src/Ryujinx/UI/Views/Main/MainMenuBarView.axaml +++ b/src/Ryujinx/UI/Views/Main/MainMenuBarView.axaml @@ -6,18 +6,16 @@ xmlns:ext="clr-namespace:Ryujinx.Ava.Common.Markup" mc:Ignorable="d" xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels" + xmlns:controls="clr-namespace:Ryujinx.Ava.UI.Controls" x:DataType="viewModels:MainWindowViewModel" x:Class="Ryujinx.Ava.UI.Views.Main.MainMenuBarView"> - + { public MainWindow Window { get; private set; } - public MainWindowViewModel ViewModel { get; private set; } public MainMenuBarView() { InitializeComponent(); - RyuLogo.IsVisible = !ConfigurationState.Instance.ShowTitleBar; - RyuLogo.Source = MainWindowViewModel.IconBitmap; - ToggleFileTypesMenuItem.ItemsSource = GenerateToggleFileTypeItems(); ChangeLanguageMenuItem.ItemsSource = GenerateLanguageMenuItems(); @@ -50,9 +48,9 @@ namespace Ryujinx.Ava.UI.Views.Main CheatManagerMenuItem.Command = Commands.CreateSilentFail(OpenCheatManagerForCurrentApp); InstallFileTypesMenuItem.Command = Commands.Create(InstallFileTypes); UninstallFileTypesMenuItem.Command = Commands.Create(UninstallFileTypes); - XciTrimmerMenuItem.Command = Commands.Create(XCITrimmerWindow.Show); - AboutWindowMenuItem.Command = Commands.Create(AboutWindow.Show); - CompatibilityListMenuItem.Command = Commands.Create(() => CompatibilityList.Show()); + XciTrimmerMenuItem.Command = Commands.Create(XciTrimmerView.Show); + AboutWindowMenuItem.Command = Commands.Create(AboutView.Show); + CompatibilityListMenuItem.Command = Commands.Create(() => CompatibilityListWindow.Show()); UpdateMenuItem.Command = MainWindowViewModel.UpdateCommand; @@ -74,7 +72,7 @@ namespace Ryujinx.Ava.UI.Views.Main { Content = $".{it.FileName}", IsChecked = it.FileType.GetConfigValue(ConfigurationState.Instance.UI.ShownFileTypes), - Command = MiniCommand.Create(() => Window.ToggleFileType(it.FileName)) + Command = Commands.Create(() => Window.ToggleFileType(it.FileName)) } ); @@ -109,7 +107,7 @@ namespace Ryujinx.Ava.UI.Views.Main Margin = new Thickness(3, 0, 3, 0), HorizontalAlignment = HorizontalAlignment.Stretch, Header = languageName, - Command = MiniCommand.Create(() => MainWindowViewModel.ChangeLanguage(language)) + Command = Commands.Create(() => MainWindowViewModel.ChangeLanguage(language)) }; yield return menuItem; @@ -132,9 +130,26 @@ namespace Ryujinx.Ava.UI.Views.Main Window.SettingsWindow = new(Window.VirtualFileSystem, Window.ContentManager); Rainbow.Enable(); - - await Window.SettingsWindow.ShowDialog(Window); - + + if (ViewModel.SelectedApplication is null) // Checks if game data exists + { + await StyleableAppWindow.ShowAsync(Window.SettingsWindow); + } + else + { + bool customConfigExists = File.Exists(Program.GetDirGameUserConfig(ViewModel.SelectedApplication.IdString)); + + if (!ViewModel.IsGameRunning || !customConfigExists) + { + await Window.SettingsWindow.ShowDialog(Window); // The game is not running, or if the user configuration does not exist + } + else + { + // If there is a custom configuration in the folder + await StyleableAppWindow.ShowAsync(new GameSpecificSettingsWindow(ViewModel, customConfigExists)); + } + } + Rainbow.Disable(); Rainbow.Reset(); @@ -180,11 +195,13 @@ namespace Ryujinx.Ava.UI.Views.Main string name = ViewModel.AppHost.Device.Processes.ActiveApplication.ApplicationControlProperties.Title[(int)ViewModel.AppHost.Device.System.State.DesiredTitleLanguage].NameString.ToString(); - await new CheatWindow( - Window.VirtualFileSystem, - ViewModel.AppHost.Device.Processes.ActiveApplication.ProgramIdText, - name, - ViewModel.SelectedApplication.Path).ShowDialog(Window); + await StyleableAppWindow.ShowAsync( + new CheatWindow( + Window.VirtualFileSystem, + ViewModel.AppHost.Device.Processes.ActiveApplication.ProgramIdText, + name, + ViewModel.SelectedApplication.Path) + ); ViewModel.AppHost.Device.EnableCheats(); } @@ -228,7 +245,7 @@ namespace Ryujinx.Ava.UI.Views.Main // Correctly size window when 'TitleBar' is enabled (Nov. 14, 2024) double barsHeight = ((Window.StatusBarHeight + Window.MenuBarHeight) + - (ConfigurationState.Instance.ShowTitleBar ? (int)Window.TitleBar.Height : 0)); + (ConfigurationState.Instance.ShowOldUI ? (int)Window.TitleBar.Height : 0)); double windowWidthScaled = (resolutionWidth * Program.WindowScaleFactor); double windowHeightScaled = ((resolutionHeight + barsHeight) * Program.WindowScaleFactor); diff --git a/src/Ryujinx/UI/Views/Main/MainStatusBarView.axaml.cs b/src/Ryujinx/UI/Views/Main/MainStatusBarView.axaml.cs index 78747013c..8341dd92a 100644 --- a/src/Ryujinx/UI/Views/Main/MainStatusBarView.axaml.cs +++ b/src/Ryujinx/UI/Views/Main/MainStatusBarView.axaml.cs @@ -1,11 +1,12 @@ using Avalonia; -using Avalonia.Controls; using Avalonia.Input; using Avalonia.Interactivity; using Avalonia.Threading; using Ryujinx.Ava.Common.Locale; +using Ryujinx.Ava.UI.Controls; +using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.Windows; -using Ryujinx.Ava.Utilities.Configuration; +using Ryujinx.Ava.Systems.Configuration; using Ryujinx.Common; using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; @@ -13,7 +14,7 @@ using System; namespace Ryujinx.Ava.UI.Views.Main { - public partial class MainStatusBarView : UserControl + public partial class MainStatusBarView : RyujinxControl { public MainWindow Window; @@ -29,7 +30,7 @@ namespace Ryujinx.Ava.UI.Views.Main if (VisualRoot is MainWindow window) { Window = window; - DataContext = window.ViewModel; + ViewModel = window.ViewModel; LocaleManager.Instance.LocaleChanged += () => Dispatcher.UIThread.Post(() => { if (Window.ViewModel.EnableNonGameRunningControls) diff --git a/src/Ryujinx/UI/Views/Main/MainViewControls.axaml.cs b/src/Ryujinx/UI/Views/Main/MainViewControls.axaml.cs index 7a83a36de..8ebc89f26 100644 --- a/src/Ryujinx/UI/Views/Main/MainViewControls.axaml.cs +++ b/src/Ryujinx/UI/Views/Main/MainViewControls.axaml.cs @@ -3,16 +3,15 @@ using Avalonia.Controls; using Avalonia.Input; using Avalonia.Interactivity; using Ryujinx.Ava.Common; +using Ryujinx.Ava.UI.Controls; using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.Windows; using System; namespace Ryujinx.Ava.UI.Views.Main { - public partial class MainViewControls : UserControl + public partial class MainViewControls : RyujinxControl { - public MainWindowViewModel ViewModel; - public MainViewControls() { InitializeComponent(); @@ -24,7 +23,7 @@ namespace Ryujinx.Ava.UI.Views.Main if (VisualRoot is MainWindow window) { - DataContext = ViewModel = window.ViewModel; + ViewModel = window.ViewModel; } } diff --git a/src/Ryujinx/UI/Controls/ApplicationGridView.axaml b/src/Ryujinx/UI/Views/Misc/ApplicationGridView.axaml similarity index 65% rename from src/Ryujinx/UI/Controls/ApplicationGridView.axaml rename to src/Ryujinx/UI/Views/Misc/ApplicationGridView.axaml index 7f7e3260b..4d1db1507 100644 --- a/src/Ryujinx/UI/Controls/ApplicationGridView.axaml +++ b/src/Ryujinx/UI/Views/Misc/ApplicationGridView.axaml @@ -1,5 +1,5 @@ - - - - + - - - - - + - + + + + @@ -86,10 +86,28 @@ Margin="5,5,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" - FontSize="16" + FontSize="18" Foreground="{DynamicResource FavoriteApplicationIconColor}" IsVisible="{Binding Favorite}" Symbol="StarFilled" /> + + + + + diff --git a/src/Ryujinx/UI/Controls/ApplicationGridView.axaml.cs b/src/Ryujinx/UI/Views/Misc/ApplicationGridView.axaml.cs similarity index 80% rename from src/Ryujinx/UI/Controls/ApplicationGridView.axaml.cs rename to src/Ryujinx/UI/Views/Misc/ApplicationGridView.axaml.cs index 22aefc3b9..c1cf7a66b 100644 --- a/src/Ryujinx/UI/Controls/ApplicationGridView.axaml.cs +++ b/src/Ryujinx/UI/Views/Misc/ApplicationGridView.axaml.cs @@ -1,13 +1,15 @@ using Avalonia.Controls; using Avalonia.Input; using Avalonia.Interactivity; +using Ryujinx.Ava.UI.Controls; using Ryujinx.Ava.UI.Helpers; -using Ryujinx.Ava.Utilities.AppLibrary; +using Ryujinx.Ava.UI.ViewModels; +using Ryujinx.Ava.Systems.AppLibrary; using System; -namespace Ryujinx.Ava.UI.Controls +namespace Ryujinx.Ava.UI.Views.Misc { - public partial class ApplicationGridView : UserControl + public partial class ApplicationGridView : RyujinxControl { public static readonly RoutedEvent ApplicationOpenedEvent = RoutedEvent.Register(nameof(ApplicationOpened), RoutingStrategies.Bubble); diff --git a/src/Ryujinx/UI/Controls/ApplicationListView.axaml b/src/Ryujinx/UI/Views/Misc/ApplicationListView.axaml similarity index 94% rename from src/Ryujinx/UI/Controls/ApplicationListView.axaml rename to src/Ryujinx/UI/Views/Misc/ApplicationListView.axaml index c6b7268b9..c9926301b 100644 --- a/src/Ryujinx/UI/Controls/ApplicationListView.axaml +++ b/src/Ryujinx/UI/Views/Misc/ApplicationListView.axaml @@ -1,21 +1,19 @@ - - - - + + { public static readonly RoutedEvent ApplicationOpenedEvent = RoutedEvent.Register(nameof(ApplicationOpened), RoutingStrategies.Bubble); @@ -32,27 +33,21 @@ namespace Ryujinx.Ava.UI.Controls private async void PlayabilityStatus_OnClick(object sender, RoutedEventArgs e) { - if (DataContext is not MainWindowViewModel mwvm) - return; - if (sender is not Button { Content: TextBlock playabilityLabel }) return; - await CompatibilityList.Show((string)playabilityLabel.Tag); + await CompatibilityListWindow.Show((string)playabilityLabel.Tag); } private async void IdString_OnClick(object sender, RoutedEventArgs e) { - if (DataContext is not MainWindowViewModel mwvm) - return; - if (sender is not Button { Content: TextBlock idText }) return; if (!RyujinxApp.IsClipboardAvailable(out IClipboard clipboard)) return; - ApplicationData appData = mwvm.Applications.FirstOrDefault(it => it.IdString == idText.Text); + ApplicationData appData = ViewModel.Applications.FirstOrDefault(it => it.IdString == idText.Text); if (appData is null) return; diff --git a/src/Ryujinx/UI/Views/Settings/SettingsGraphicsView.axaml b/src/Ryujinx/UI/Views/Settings/SettingsGraphicsView.axaml index 42515a4e9..219efcef8 100644 --- a/src/Ryujinx/UI/Views/Settings/SettingsGraphicsView.axaml +++ b/src/Ryujinx/UI/Views/Settings/SettingsGraphicsView.axaml @@ -8,7 +8,6 @@ xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" xmlns:ext="clr-namespace:Ryujinx.Ava.Common.Markup" xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels" - xmlns:helper="clr-namespace:Ryujinx.Common.Helper;assembly=Ryujinx.Common" Design.Width="1000" mc:Ignorable="d" x:DataType="viewModels:SettingsViewModel"> @@ -34,24 +33,16 @@ ToolTip.Tip="{ext:Locale SettingsTabGraphicsBackendTooltip}" Text="{ext:Locale SettingsTabGraphicsBackend}" Width="250" /> - - - - + - - - diff --git a/src/Ryujinx/UI/Views/Settings/SettingsHacksView.axaml b/src/Ryujinx/UI/Views/Settings/SettingsHacksView.axaml index 9c597d65f..229f9e866 100644 --- a/src/Ryujinx/UI/Views/Settings/SettingsHacksView.axaml +++ b/src/Ryujinx/UI/Views/Settings/SettingsHacksView.axaml @@ -29,7 +29,7 @@ + Text="Highly specific hacks & tricks to alleviate performance issues, crashing, or freezing. Can cause issues." /> + ToolTip.Tip="{Binding DirtyHacks.NifmDisableIsAnyInternetRequestAcceptedTooltip}"> - + IsChecked="{Binding DirtyHacks.NifmDisableIsAnyInternetRequestAccepted}"/> + - - - - - diff --git a/src/Ryujinx/UI/Views/Settings/SettingsHotkeysView.axaml.cs b/src/Ryujinx/UI/Views/Settings/SettingsHotkeysView.axaml.cs index d3d1537e0..46693374a 100644 --- a/src/Ryujinx/UI/Views/Settings/SettingsHotkeysView.axaml.cs +++ b/src/Ryujinx/UI/Views/Settings/SettingsHotkeysView.axaml.cs @@ -3,7 +3,9 @@ using Avalonia.Controls.Primitives; using Avalonia.Input; using Avalonia.Interactivity; using Avalonia.LogicalTree; +using Avalonia.Threading; using Ryujinx.Ava.Input; +using Ryujinx.Ava.UI.Controls; using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Input; @@ -13,7 +15,7 @@ using Key = Ryujinx.Common.Configuration.Hid.Key; namespace Ryujinx.Ava.UI.Views.Settings { - public partial class SettingsHotkeysView : UserControl + public partial class SettingsHotkeysView : RyujinxControl { private ButtonKeyAssigner _currentAssigner; private readonly IGamepadDriver _avaloniaKeyboardDriver; @@ -78,45 +80,49 @@ namespace Ryujinx.Ava.UI.Views.Settings { if (e.ButtonValue.HasValue) { - SettingsViewModel viewModel = (DataContext) as SettingsViewModel; Button buttonValue = e.ButtonValue.Value; - switch (button.Name) + Dispatcher.UIThread.Post(() => { - case "ToggleVSyncMode": - viewModel.KeyboardHotkey.ToggleVSyncMode = buttonValue.AsHidType(); - break; - case "Screenshot": - viewModel.KeyboardHotkey.Screenshot = buttonValue.AsHidType(); - break; - case "ShowUI": - viewModel.KeyboardHotkey.ShowUI = buttonValue.AsHidType(); - break; - case "Pause": - viewModel.KeyboardHotkey.Pause = buttonValue.AsHidType(); - break; - case "ToggleMute": - viewModel.KeyboardHotkey.ToggleMute = buttonValue.AsHidType(); - break; - case "ResScaleUp": - viewModel.KeyboardHotkey.ResScaleUp = buttonValue.AsHidType(); - break; - case "ResScaleDown": - viewModel.KeyboardHotkey.ResScaleDown = buttonValue.AsHidType(); - break; - case "VolumeUp": - viewModel.KeyboardHotkey.VolumeUp = buttonValue.AsHidType(); - break; - case "VolumeDown": - viewModel.KeyboardHotkey.VolumeDown = buttonValue.AsHidType(); - break; - case "CustomVSyncIntervalIncrement": - viewModel.KeyboardHotkey.CustomVSyncIntervalIncrement = buttonValue.AsHidType(); - break; - case "CustomVSyncIntervalDecrement": - viewModel.KeyboardHotkey.CustomVSyncIntervalDecrement = buttonValue.AsHidType(); - break; - } + switch (button.Name) + { + case "ToggleVSyncMode": + ViewModel.KeyboardHotkey.ToggleVSyncMode = buttonValue.AsHidType(); + break; + case "Screenshot": + ViewModel.KeyboardHotkey.Screenshot = buttonValue.AsHidType(); + break; + case "ShowUI": + ViewModel.KeyboardHotkey.ShowUI = buttonValue.AsHidType(); + break; + case "Pause": + ViewModel.KeyboardHotkey.Pause = buttonValue.AsHidType(); + break; + case "ToggleMute": + ViewModel.KeyboardHotkey.ToggleMute = buttonValue.AsHidType(); + break; + case "ResScaleUp": + ViewModel.KeyboardHotkey.ResScaleUp = buttonValue.AsHidType(); + break; + case "ResScaleDown": + ViewModel.KeyboardHotkey.ResScaleDown = buttonValue.AsHidType(); + break; + case "VolumeUp": + ViewModel.KeyboardHotkey.VolumeUp = buttonValue.AsHidType(); + break; + case "VolumeDown": + ViewModel.KeyboardHotkey.VolumeDown = buttonValue.AsHidType(); + break; + case "CustomVSyncIntervalIncrement": + ViewModel.KeyboardHotkey.CustomVSyncIntervalIncrement = + buttonValue.AsHidType(); + break; + case "CustomVSyncIntervalDecrement": + ViewModel.KeyboardHotkey.CustomVSyncIntervalDecrement = + buttonValue.AsHidType(); + break; + } + }); } }; diff --git a/src/Ryujinx/UI/Views/Settings/SettingsInputView.axaml b/src/Ryujinx/UI/Views/Settings/SettingsInputView.axaml index b0edc51a5..0a77c8863 100644 --- a/src/Ryujinx/UI/Views/Settings/SettingsInputView.axaml +++ b/src/Ryujinx/UI/Views/Settings/SettingsInputView.axaml @@ -21,12 +21,7 @@ - - - - - - + diff --git a/src/Ryujinx/UI/Views/Settings/SettingsNetworkView.axaml.cs b/src/Ryujinx/UI/Views/Settings/SettingsNetworkView.axaml.cs index c4987702c..934fcf142 100644 --- a/src/Ryujinx/UI/Views/Settings/SettingsNetworkView.axaml.cs +++ b/src/Ryujinx/UI/Views/Settings/SettingsNetworkView.axaml.cs @@ -1,15 +1,13 @@ -using Avalonia.Controls; using Avalonia.Interactivity; +using Ryujinx.Ava.UI.Controls; using Ryujinx.Ava.UI.ViewModels; using System; namespace Ryujinx.Ava.UI.Views.Settings { - public partial class SettingsNetworkView : UserControl + public partial class SettingsNetworkView : RyujinxControl { private readonly Random _random; - - public SettingsViewModel ViewModel; public SettingsNetworkView() { diff --git a/src/Ryujinx/UI/Views/Settings/SettingsSystemView.axaml b/src/Ryujinx/UI/Views/Settings/SettingsSystemView.axaml index 5daa7f69f..a52fe5fbe 100644 --- a/src/Ryujinx/UI/Views/Settings/SettingsSystemView.axaml +++ b/src/Ryujinx/UI/Views/Settings/SettingsSystemView.axaml @@ -156,6 +156,8 @@ ValueMemberBinding="{Binding Mode=OneWay, Converter={x:Static helpers:TimeZoneConverter.Instance}}" /> + + - - + + ToolTip.Tip="{ext:Locale MatchTimeTooltip}"> + + + { - public SettingsViewModel ViewModel; - public SettingsSystemView() { InitializeComponent(); diff --git a/src/Ryujinx/UI/Views/Settings/SettingsUIView.axaml b/src/Ryujinx/UI/Views/Settings/SettingsUIView.axaml index 347f0fb51..ad05efd06 100644 --- a/src/Ryujinx/UI/Views/Settings/SettingsUIView.axaml +++ b/src/Ryujinx/UI/Views/Settings/SettingsUIView.axaml @@ -6,7 +6,6 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:ext="clr-namespace:Ryujinx.Ava.Common.Markup" xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels" - xmlns:helper="clr-namespace:Ryujinx.Common.Helper;assembly=Ryujinx.Common" mc:Ignorable="d" x:DataType="viewModels:SettingsViewModel"> @@ -19,211 +18,265 @@ HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - + Margin="10" + Spacing="10" + Grid.Column="2" + Orientation="Vertical" HorizontalAlignment="Stretch"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + diff --git a/src/Ryujinx/UI/Views/Settings/SettingsUIView.axaml.cs b/src/Ryujinx/UI/Views/Settings/SettingsUIView.axaml.cs index 2b0e57cb5..fb839a473 100644 --- a/src/Ryujinx/UI/Views/Settings/SettingsUIView.axaml.cs +++ b/src/Ryujinx/UI/Views/Settings/SettingsUIView.axaml.cs @@ -3,10 +3,10 @@ using Avalonia.Controls; using Avalonia.Interactivity; using Avalonia.Platform.Storage; using Gommon; +using Ryujinx.Ava.UI.Controls; using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.Utilities; -using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -14,20 +14,18 @@ using System.Threading.Tasks; namespace Ryujinx.Ava.UI.Views.Settings { - public partial class SettingsUiView : UserControl + public partial class SettingsUiView : RyujinxControl { - public SettingsViewModel ViewModel; - public SettingsUiView() { InitializeComponent(); AddGameDirButton.Command = - Commands.Create(() => AddDirButton(GameDirPathBox, ViewModel.GameDirectories, true)); + Commands.Create(() => AddDirButton(GameDirPathBox, ViewModel.GameDirectories)); AddAutoloadDirButton.Command = - Commands.Create(() => AddDirButton(AutoloadDirPathBox, ViewModel.AutoloadDirectories, false)); + Commands.Create(() => AddDirButton(AutoloadDirPathBox, ViewModel.AutoloadDirectories)); } - private async Task AddDirButton(TextBox addDirBox, AvaloniaList directories, bool isGameList) + private async Task AddDirButton(TextBox addDirBox, AvaloniaList directories) { string path = addDirBox.Text; diff --git a/src/Ryujinx/UI/Views/User/UserEditorView.axaml b/src/Ryujinx/UI/Views/User/UserEditorView.axaml index 8b5375eb4..47f6a60e1 100644 --- a/src/Ryujinx/UI/Views/User/UserEditorView.axaml +++ b/src/Ryujinx/UI/Views/User/UserEditorView.axaml @@ -14,15 +14,7 @@ mc:Ignorable="d" Focusable="True" x:DataType="models:TempProfile"> - - - - - - - - - + { private NavigationDialogHost _parent; private UserProfile _profile; private bool _isNewUser; - - public TempProfile TempProfile { get; set; } + public static uint MaxProfileNameLength => 0x20; public bool IsDeletable => _profile.UserId != AccountManager.DefaultUserId; @@ -42,7 +40,7 @@ namespace Ryujinx.Ava.UI.Views.User (NavigationDialogHost parent, UserProfile profile, bool isNewUser) = ((NavigationDialogHost parent, UserProfile profile, bool isNewUser))arg.Parameter; _isNewUser = isNewUser; _profile = profile; - TempProfile = new TempProfile(_profile); + ViewModel = new TempProfile(_profile); _parent = parent; break; @@ -51,8 +49,6 @@ namespace Ryujinx.Ava.UI.Views.User ((ContentDialog)_parent.Parent).Title = $"{LocaleManager.Instance[LocaleKeys.UserProfileWindowTitle]} - " + $"{(_isNewUser ? LocaleManager.Instance[LocaleKeys.UserEditorTitleCreate] : LocaleManager.Instance[LocaleKeys.UserEditorTitle])}"; - DataContext = TempProfile; - AddPictureButton.IsVisible = _isNewUser; ChangePictureButton.IsVisible = !_isNewUser; IdLabel.IsVisible = _profile != null; @@ -72,7 +68,7 @@ namespace Ryujinx.Ava.UI.Views.User { if (_isNewUser) { - if (TempProfile.Name != String.Empty || TempProfile.Image != null) + if (ViewModel.Name != string.Empty || ViewModel.Image != null) { if (await ContentDialogHelper.CreateChoiceDialog( LocaleManager.Instance[LocaleKeys.DialogUserProfileUnsavedChangesTitle], @@ -89,7 +85,7 @@ namespace Ryujinx.Ava.UI.Views.User } else { - if (_profile.Name != TempProfile.Name || _profile.Image != TempProfile.Image) + if (_profile.Name != ViewModel.Name || _profile.Image != ViewModel.Image) { if (await ContentDialogHelper.CreateChoiceDialog( LocaleManager.Instance[LocaleKeys.DialogUserProfileUnsavedChangesTitle], @@ -115,31 +111,31 @@ namespace Ryujinx.Ava.UI.Views.User { DataValidationErrors.ClearErrors(NameBox); - if (string.IsNullOrWhiteSpace(TempProfile.Name)) + if (string.IsNullOrWhiteSpace(ViewModel.Name)) { DataValidationErrors.SetError(NameBox, new DataValidationException(LocaleManager.Instance[LocaleKeys.UserProfileEmptyNameError])); return; } - if (TempProfile.Image == null) + if (ViewModel.Image == null) { - _parent.Navigate(typeof(UserProfileImageSelectorView), (_parent, TempProfile)); + _parent.Navigate(typeof(UserProfileImageSelectorView), (_parent, ViewModel)); return; } if (_profile != null && !_isNewUser) { - _profile.Name = TempProfile.Name; - _profile.Image = TempProfile.Image; + _profile.Name = ViewModel.Name; + _profile.Image = ViewModel.Image; _profile.UpdateState(); _parent.AccountManager.SetUserName(_profile.UserId, _profile.Name); _parent.AccountManager.SetUserImage(_profile.UserId, _profile.Image); } else if (_isNewUser) { - _parent.AccountManager.AddUser(TempProfile.Name, TempProfile.Image, TempProfile.UserId); + _parent.AccountManager.AddUser(ViewModel.Name, ViewModel.Image, ViewModel.UserId); } else { @@ -151,7 +147,7 @@ namespace Ryujinx.Ava.UI.Views.User public void SelectProfileImage() { - _parent.Navigate(typeof(UserProfileImageSelectorView), (_parent, TempProfile)); + _parent.Navigate(typeof(UserProfileImageSelectorView), (_parent, ViewModel)); } private void ChangePictureButton_Click(object sender, RoutedEventArgs e) diff --git a/src/Ryujinx/UI/Views/User/UserFirmwareAvatarSelectorView.axaml b/src/Ryujinx/UI/Views/User/UserFirmwareAvatarSelectorView.axaml index b6442918b..c22624fd5 100644 --- a/src/Ryujinx/UI/Views/User/UserFirmwareAvatarSelectorView.axaml +++ b/src/Ryujinx/UI/Views/User/UserFirmwareAvatarSelectorView.axaml @@ -20,13 +20,7 @@ - - - - - - + VerticalAlignment="Stretch" RowDefinitions="Auto,*,Auto,Auto"> { private NavigationDialogHost _parent; private TempProfile _profile; @@ -20,8 +19,6 @@ namespace Ryujinx.Ava.UI.Views.User { ContentManager = contentManager; - DataContext = ViewModel; - InitializeComponent(); } @@ -55,8 +52,6 @@ namespace Ryujinx.Ava.UI.Views.User public ContentManager ContentManager { get; private set; } - internal UserFirmwareAvatarSelectorViewModel ViewModel { get; set; } - private void GoBack(object sender, RoutedEventArgs e) { _parent.GoBack(); diff --git a/src/Ryujinx/UI/Views/User/UserProfileImageSelectorView.axaml b/src/Ryujinx/UI/Views/User/UserProfileImageSelectorView.axaml index 26e27176b..03aebec8f 100644 --- a/src/Ryujinx/UI/Views/User/UserProfileImageSelectorView.axaml +++ b/src/Ryujinx/UI/Views/User/UserProfileImageSelectorView.axaml @@ -17,12 +17,7 @@ - - - - - + VerticalAlignment="Center" RowDefinitions="Auto,70,Auto"> { private ContentManager _contentManager; private NavigationDialogHost _parent; private TempProfile _profile; - internal UserProfileImageSelectorViewModel ViewModel { get; private set; } - public UserProfileImageSelectorView() { InitializeComponent(); diff --git a/src/Ryujinx/UI/Views/User/UserRecovererView.axaml b/src/Ryujinx/UI/Views/User/UserRecovererView.axaml index f49444642..43d84787d 100644 --- a/src/Ryujinx/UI/Views/User/UserRecovererView.axaml +++ b/src/Ryujinx/UI/Views/User/UserRecovererView.axaml @@ -18,11 +18,7 @@ - - - - + VerticalAlignment="Stretch" RowDefinitions="*,Auto"> - - - - - + { private NavigationDialogHost _parent; diff --git a/src/Ryujinx/UI/Views/User/UserSaveManagerView.axaml b/src/Ryujinx/UI/Views/User/UserSaveManagerView.axaml index 576b15731..f04b8a7a3 100644 --- a/src/Ryujinx/UI/Views/User/UserSaveManagerView.axaml +++ b/src/Ryujinx/UI/Views/User/UserSaveManagerView.axaml @@ -19,19 +19,10 @@ - - - - - - + - - - - + HorizontalAlignment="Stretch" ColumnDefinitions="Auto,*"> - - - - + Margin="10,0, 0, 0" ColumnDefinitions="Auto,*">