diff --git a/Directory.Packages.props b/Directory.Packages.props index f1d7cac61..62a642374 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -42,11 +42,10 @@ - + - diff --git a/README.md b/README.md index bb51dee13..219e0e598 100644 --- a/README.md +++ b/README.md @@ -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 7653ced9e..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 @@ -969,6 +970,7 @@ 0100751007ADA000,"Don't Starve: Nintendo Switch Edition",nvdec,playable,2022-02-05 20:43:34 010088B010DD2000,"Dongo Adventure",,playable,2022-10-04 16:22:26 0100C1F0051B6000,"Donkey Kong Country™: Tropical Freeze",,playable,2024-08-05 16:46:10 +01009D901BC56000,"Donkey Kong Country™: Returns HD",gpu,ingame,2025-02-16 13:44:12 0100F2C00F060000,"Doodle Derby",,boots,2020-12-04 22:51:48 0100416004C00000,"DOOM",gpu;slow;nvdec;online-broken,ingame,2024-09-23 15:40:07 010018900DD00000,"DOOM (1993)",nvdec;online-broken,menus,2022-09-06 13:32:19 @@ -1158,7 +1160,7 @@ 010095600AA36000,"Fill-a-Pix: Phil's Epic Adventure",,playable,2020-12-22 13:48:22 0100C3A00BB76000,"Fimbul",nvdec,playable,2022-07-26 13:31:47 0100C8200E942000,"Fin and the Ancient Mystery",nvdec,playable,2020-12-17 16:40:39 -01000EA014150000,"FINAL FANTASY",crash,nothing,2024-09-05 20:55:30 +01000EA014150000,"FINAL FANTASY",,playable,2025-02-16 21:27:30 01006B7014156000,"FINAL FANTASY II",crash,nothing,2024-04-13 19:18:04 01006F000B056000,"FINAL FANTASY IX",audout;nvdec,playable,2021-06-05 11:35:00 0100AA201415C000,"FINAL FANTASY V",,playable,2023-04-26 01:11:55 @@ -1381,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 @@ -1435,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 @@ -1799,6 +1804,7 @@ 010005A00B312000,"Megaton Rainfall",gpu;opengl,boots,2022-08-04 18:29:43 0100EA100DF92000,"Meiji Katsugeki Haikara Ryuuseigumi - Seibai Shimaseu, Yonaoshi Kagyou",32-bit;nvdec,playable,2022-12-05 13:19:12 0100B360068B2000,"Mekorama",gpu,boots,2021-06-17 16:37:21 +010012301932A000,"Melatonin",,playable,2025-02-16 04:08:17 01000FA010340000,"Melbits World",nvdec;online,menus,2021-11-26 13:51:22 0100F68019636000,"Melon Journey",,playable,2023-04-23 21:20:01 010079C012896000,"Memories Off -Innocent Fille- for Dearest",,playable,2020-08-04 07:31:22 @@ -2727,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 @@ -2838,6 +2844,7 @@ 01009B90006DC000,"Super Mario Maker™ 2",online-broken;ldn-broken,playable,2024-08-25 11:05:19 0100000000010000,"Super Mario Odyssey™",nvdec;intel-vendor-bug;mac-bug,playable,2024-08-25 01:32:34 010036B0034E4000,"Super Mario Party™",gpu;Needs Update;ldn-works,ingame,2024-06-21 05:10:16 +0100965017338000,"Super Mario Party Jamboree",mac-bug;gpu,ingame,2025-02-17 02:09:20 0100BC0018138000,"Super Mario RPG™",gpu;audio;nvdec,ingame,2024-06-19 17:43:42 ,"Super Mario World",homebrew,boots,2024-06-13 01:40:31 010049900F546000,"Super Mario™ 3D All-Stars",services-horizon;slow;vulkan;amd-vendor-bug,ingame,2024-05-07 02:38:16 diff --git a/src/ARMeilleure/Translation/Cache/JitCache.cs b/src/ARMeilleure/Translation/Cache/JitCache.cs index bc75e7d3f..e480985b1 100644 --- a/src/ARMeilleure/Translation/Cache/JitCache.cs +++ b/src/ARMeilleure/Translation/Cache/JitCache.cs @@ -24,14 +24,14 @@ namespace ARMeilleure.Translation.Cache private static JitCacheInvalidation _jitCacheInvalidator; - private static CacheMemoryAllocator _cacheAllocator; + private static List _cacheAllocators = []; private static readonly List _cacheEntries = []; private static readonly Lock _lock = new(); private static bool _initialized; - private static readonly List _jitRegions = new(); + private static readonly List _jitRegions = []; private static int _activeRegionIndex = 0; [SupportedOSPlatform("windows")] @@ -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; - var newRegion = new ReservedRegion(_jitRegions[0].Allocator, CacheSize); + 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} ({((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/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/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 76d873f60..82be1572f 100644 --- a/src/Ryujinx.Common/TitleIDs.cs +++ b/src/Ryujinx.Common/TitleIDs.cs @@ -10,55 +10,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: - "01007300020fa000", // Astral Chain - "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"; @@ -230,6 +181,7 @@ namespace Ryujinx.Common "01008c8012920000", // Dying Light Platinum Edition "01001cc01b2d4000", // Goat Simulator 3 "01003620068ea000", // Hand of Fate 2 + "0100f7e00c70e000", // Hogwarts Legacy "010085500130a000", // Lego City: Undercover "010073c01af34000", // LEGO Horizon Adventures "0100d71004694000", // Minecraft diff --git a/src/Ryujinx.Cpu/LightningJit/Cache/JitCache.cs b/src/Ryujinx.Cpu/LightningJit/Cache/JitCache.cs index ccf8ad964..96bb05819 100644 --- a/src/Ryujinx.Cpu/LightningJit/Cache/JitCache.cs +++ b/src/Ryujinx.Cpu/LightningJit/Cache/JitCache.cs @@ -27,7 +27,7 @@ namespace Ryujinx.Cpu.LightningJit.Cache private static readonly Lock _lock = new(); private static bool _initialized; - private static readonly List _jitRegions = new(); + private static readonly List _jitRegions = []; private static int _activeRegionIndex = 0; [SupportedOSPlatform("windows")] @@ -166,7 +166,7 @@ namespace Ryujinx.Cpu.LightningJit.Cache int newRegionNumber = _activeRegionIndex; - Logger.Warning?.Print(LogClass.Cpu, $"JIT Cache Region {exhaustedRegion} exhausted, creating new Cache Region {newRegionNumber} ({((newRegionNumber + 1) * CacheSize).Bytes()} Total Allocation)."); + 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); 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/Engine/Threed/ComputeDraw/VtgAsComputeContext.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeContext.cs index 15f1a4a33..eba1b6030 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeContext.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeContext.cs @@ -11,6 +11,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw /// class VtgAsComputeContext : IDisposable { + private const int DummyBufferSize = 16; + private readonly GpuContext _context; /// @@ -46,7 +48,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw 1, 1, 1, - format.GetBytesPerElement(), + 1, format, DepthStencilMode.Depth, Target.TextureBuffer, @@ -519,6 +521,21 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw return new BufferRange(_geometryIndexDataBuffer.Handle, offset, size, write); } + /// + /// Gets the range for a dummy 16 bytes buffer, filled with zeros. + /// + /// Dummy buffer range + public BufferRange GetDummyBufferRange() + { + if (_dummyBuffer == BufferHandle.Null) + { + _dummyBuffer = _context.Renderer.CreateBuffer(DummyBufferSize, BufferAccess.DeviceMemory); + _context.Renderer.Pipeline.ClearBuffer(_dummyBuffer, 0, DummyBufferSize, 0); + } + + return new BufferRange(_dummyBuffer, 0, DummyBufferSize); + } + /// /// Gets the range for a sequential index buffer, with ever incrementing index values. /// diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeState.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeState.cs index 16a19debe..0d59275ff 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeState.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeState.cs @@ -147,6 +147,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw { _vacContext.VertexInfoBufferUpdater.SetVertexStride(index, 0, componentsCount); _vacContext.VertexInfoBufferUpdater.SetVertexOffset(index, 0, 0); + SetDummyBufferTexture(_vertexAsCompute.Reservations, index, format); continue; } @@ -162,12 +163,15 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw { _vacContext.VertexInfoBufferUpdater.SetVertexStride(index, 0, componentsCount); _vacContext.VertexInfoBufferUpdater.SetVertexOffset(index, 0, 0); + SetDummyBufferTexture(_vertexAsCompute.Reservations, index, format); continue; } int vbStride = vertexBuffer.UnpackStride(); ulong vbSize = GetVertexBufferSize(address, endAddress.Pack(), vbStride, _indexed, instanced, _firstVertex, _count); + ulong oldVbSize = vbSize; + ulong attributeOffset = (ulong)vertexAttrib.UnpackOffset(); int componentSize = format.GetScalarSize(); @@ -340,6 +344,20 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw return maxOutputVertices / verticesPerPrimitive; } + /// + /// Binds a dummy buffer as vertex buffer into a buffer texture. + /// + /// Shader resource binding reservations + /// Buffer texture index + /// Buffer texture format + private readonly void SetDummyBufferTexture(ResourceReservations reservations, int index, Format format) + { + ITexture bufferTexture = _vacContext.EnsureBufferTexture(index + 2, format); + bufferTexture.SetStorage(_vacContext.GetDummyBufferRange()); + + _context.Renderer.Pipeline.SetTextureAndSampler(ShaderStage.Compute, reservations.GetVertexBufferTextureBinding(index), bufferTexture, null); + } + /// /// Binds a vertex buffer into a buffer texture. /// diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs index a2f391f25..e3b981f40 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs @@ -913,7 +913,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed Span> scissors = [ - new Rectangle(scissorX, scissorY, scissorW, scissorH) + new(scissorX, scissorY, scissorW, scissorH) ]; _context.Renderer.Pipeline.SetScissors(scissors); 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 acd93e09e..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++) { @@ -729,7 +720,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache ShaderProgram program = translatorContext.Translate(); - CachedShaderStage[] shaders = [new CachedShaderStage(program.Info, shader.Code, shader.Cb1Data)]; + CachedShaderStage[] shaders = [new(program.Info, shader.Code, shader.Cb1Data)]; _compilationQueue.Enqueue(new ProgramCompilation([program], shaders, newSpecState, programIndex, isCompute: true)); } diff --git a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs index c7d86a47e..393584bd4 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs @@ -17,7 +17,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; @@ -39,7 +39,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; @@ -117,10 +117,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 476ae3d82..4fc66c4c0 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); } @@ -735,7 +731,7 @@ namespace Ryujinx.Graphics.Gpu.Shader { ulong cb1DataAddress = channel.BufferManager.GetGraphicsUniformBufferAddress(0, 1); - MemoryManager memoryManager = channel.MemoryManager; + var memoryManager = channel.MemoryManager; codeA ??= memoryManager.GetSpan(vertexA.Address, vertexA.Size).ToArray(); codeB ??= memoryManager.GetSpan(currentStage.Address, currentStage.Size).ToArray(); @@ -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; 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) { - return []; + return Array.Empty(); } return memoryManager.Physical.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.Nvdec.Vp9/BitDepth.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/BitDepth.cs index fdf0aff9c..1f0c65c7b 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/BitDepth.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/BitDepth.cs @@ -6,4 +6,4 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 Bits10 = 10, // < 10 bits Bits12 = 12, // < 12 bits } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/CodecErr.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/CodecErr.cs index 3bd149df4..9bcf4ac01 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/CodecErr.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/CodecErr.cs @@ -1,56 +1,75 @@ -namespace Ryujinx.Graphics.Nvdec.Vp9 +namespace Ryujinx.Graphics.Nvdec.Vp9 { internal enum CodecErr { - /*!\brief Operation completed without error */ - CodecOk, + /// + /// Operation completed without error + /// + Ok, - /*!\brief Unspecified error */ - CodecError, + /// + /// Unspecified error + /// + Error, - /*!\brief Memory operation failed */ - CodecMemError, + /// + /// Memory operation failed + /// + MemError, - /*!\brief ABI version mismatch */ - CodecAbiMismatch, + /// + /// ABI version mismatch + /// + AbiMismatch, - /*!\brief Algorithm does not have required capability */ - CodecIncapable, + /// + /// Algorithm does not have required capability + /// + Incapable, - /*!\brief The given bitstream is not supported. - * - * The bitstream was unable to be parsed at the highest level. The decoder - * is unable to proceed. This error \ref SHOULD be treated as fatal to the - * stream. */ - CodecUnsupBitstream, + /// + /// The given bitstream is not supported. + /// + /// + /// The bitstream was unable to be parsed at the highest level.
+ /// The decoder is unable to proceed.
+ /// This error SHOULD be treated as fatal to the stream. + ///
+ UnsupBitstream, - /*!\brief Encoded bitstream uses an unsupported feature - * - * The decoder does not implement a feature required by the encoder. This - * return code should only be used for features that prevent future - * pictures from being properly decoded. This error \ref MAY be treated as - * fatal to the stream or \ref MAY be treated as fatal to the current GOP. - */ - CodecUnsupFeature, + /// + /// Encoded bitstream uses an unsupported feature + /// + /// + /// The decoder does not implement a feature required by the encoder.
+ /// This return code should only be used for features that prevent future + /// pictures from being properly decoded.
+ ///
+ /// This error MAY be treated as fatal to the stream or MAY be treated as fatal to the current GOP. + ///
+ UnsupFeature, - /*!\brief The coded data for this stream is corrupt or incomplete - * - * There was a problem decoding the current frame. This return code - * should only be used for failures that prevent future pictures from - * being properly decoded. This error \ref MAY be treated as fatal to the - * stream or \ref MAY be treated as fatal to the current GOP. If decoding - * is continued for the current GOP, artifacts may be present. - */ - CodecCorruptFrame, + /// + /// The coded data for this stream is corrupt or incomplete. + /// + /// + /// There was a problem decoding the current frame.
+ /// This return code should only be used + /// for failures that prevent future pictures from being properly decoded.
+ ///
+ /// This error MAY be treated as fatal to the stream or MAY be treated as fatal to the current GOP.
+ /// If decoding is continued for the current GOP, artifacts may be present. + ///
+ CorruptFrame, - /*!\brief An application-supplied parameter is not valid. - * - */ - CodecInvalidParam, + /// + /// An application-supplied parameter is not valid. + /// + InvalidParam, - /*!\brief An iterator reached the end of list. - * - */ - CodecListEnd, + /// + /// An iterator reached the end of list. + /// + ListEnd } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Common/BitUtils.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Common/BitUtils.cs index 807f1293e..900b9d1c1 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Common/BitUtils.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Common/BitUtils.cs @@ -10,7 +10,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Common [MethodImpl(MethodImplOptions.AggressiveInlining)] public static byte ClipPixel(int val) { - return (byte)((val > 255) ? 255 : (val < 0) ? 0 : val); + return (byte)(val > 255 ? 255 : val < 0 ? 0 : val); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -56,4 +56,4 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Common return numValues > 0 ? GetMsb(numValues) + 1 : 0; } } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Common/MemoryAllocator.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Common/MemoryAllocator.cs index 18ed172f8..6d108d86e 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Common/MemoryAllocator.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Common/MemoryAllocator.cs @@ -51,6 +51,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Common { Marshal.FreeHGlobal(item.Pointer); } + item.Pointer = ptr; item.Length = lengthInBytes; break; @@ -58,7 +59,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Common } } - return new ArrayPtr(ptr, length); + ArrayPtr allocation = new(ptr, length); + + allocation.AsSpan().Fill(default); + + return allocation; } public unsafe void Free(ArrayPtr arr) where T : unmanaged diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Common/MemoryUtil.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Common/MemoryUtil.cs index afdc6b0b2..66c41f8cc 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Common/MemoryUtil.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Common/MemoryUtil.cs @@ -20,4 +20,4 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Common new Span(ptr, length).Fill(value); } } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Constants.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Constants.cs index 17efb203b..4d7f919e5 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Constants.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Constants.cs @@ -1,8 +1,10 @@ +using Ryujinx.Graphics.Nvdec.Vp9.Types; + namespace Ryujinx.Graphics.Nvdec.Vp9 { internal static class Constants { - public const int Vp9InterpExtend = 4; + public const int InterpExtend = 4; public const int MaxMbPlane = 3; @@ -25,6 +27,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 /* Segment Feature Masks */ public const int MaxMvRefCandidates = 2; + public const int IntraInterContexts = 4; public const int CompInterContexts = 5; public const int RefContexts = 5; @@ -32,12 +35,26 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 public const int EightTapSmooth = 1; public const int EightTapSharp = 2; public const int SwitchableFilters = 3; /* Number of switchable filters */ + public const int Bilinear = 3; - public const int Switchable = 4; /* should be the last one */ + + // The codec can operate in four possible inter prediction filter mode: + // 8-tap, 8-tap-smooth, 8-tap-sharp, and switching between the three. + public const int SwitchableFilterContexts = SwitchableFilters + 1; + public const int Switchable = 4; /* Should be the last one */ // Frame public const int RefsPerFrame = 3; + public const int RefFramesLog2 = 3; + public const int RefFrames = 1 << RefFramesLog2; + + // 1 scratch frame for the new frame, 3 for scaled references on the encoder. + public const int FrameBuffers = RefFrames + 4; + + public const int FrameContextsLog2 = 2; + public const int FrameContexts = 1 << FrameContextsLog2; + public const int NumPingPongBuffers = 2; public const int Class0Bits = 1; /* bits at integer precision for class 0 */ @@ -48,9 +65,9 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 public const int MvLow = -(1 << MvInUseBits); // Coefficient token alphabet - public const int ZeroToken = 0; // 0 Extra Bits 0+0 - public const int OneToken = 1; // 1 Extra Bits 0+1 - public const int TwoToken = 2; // 2 Extra Bits 0+1 + public const int ZeroToken = 0; // 0 Extra Bits 0+0 + public const int OneToken = 1; // 1 Extra Bits 0+1 + public const int TwoToken = 2; // 2 Extra Bits 0+1 public const int PivotNode = 2; @@ -65,5 +82,19 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 public const int SegmentAbsData = 1; public const int MaxSegments = 8; + + public const int PartitionTypes = (int)PartitionType.PartitionTypes; + + public const int PartitionPlOffset = 4; // Number of probability models per block size + public const int PartitionContexts = 4 * PartitionPlOffset; + + public const int PlaneTypes = (int)PlaneType.PlaneTypes; + + public const int IntraModes = (int)PredictionMode.TmPred + 1; + + public const int InterModes = 1 + (int)PredictionMode.NewMv - (int)PredictionMode.NearestMv; + + public const int SkipContexts = 3; + public const int InterModeContexts = 7; } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/DSubExp.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/DSubExp.cs new file mode 100644 index 000000000..72cfba8f3 --- /dev/null +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/DSubExp.cs @@ -0,0 +1,47 @@ +using System.Diagnostics; + +namespace Ryujinx.Graphics.Nvdec.Vp9 +{ + internal static class DSubExp + { + public static int InvRecenterNonneg(int v, int m) + { + if (v > 2 * m) + { + return v; + } + + return (v & 1) != 0 ? m - ((v + 1) >> 1) : m + (v >> 1); + } + + private static readonly byte[] _invMapTable = + [ + 7, 20, 33, 46, 59, 72, 85, 98, 111, 124, 137, 150, 163, 176, 189, 202, 215, 228, 241, 254, 1, 2, 3, 4, + 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 60, 61, 62, + 63, 64, 65, 66, 67, 68, 69, 70, 71, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 86, 87, 88, 89, 90, + 91, 92, 93, 94, 95, 96, 97, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 112, 113, 114, + 115, 116, 117, 118, 119, 120, 121, 122, 123, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, + 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 151, 152, 153, 154, 155, 156, 157, 158, 159, + 160, 161, 162, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 177, 178, 179, 180, 181, 182, + 183, 184, 185, 186, 187, 188, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 203, 204, 205, + 206, 207, 208, 209, 210, 211, 212, 213, 214, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, + 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 242, 243, 244, 245, 246, 247, 248, 249, 250, + 251, 252, 253, 253 + ]; + + public static int InvRemapProb(int v, int m) + { + Debug.Assert(v < _invMapTable.Length / sizeof(byte)); + + v = _invMapTable[v]; + m--; + if (m << 1 <= Prob.MaxProb) + { + return 1 + InvRecenterNonneg(v, m); + } + + return Prob.MaxProb - InvRecenterNonneg(v, Prob.MaxProb - 1 - m); + } + } +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/DecodeFrame.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/DecodeFrame.cs index 5f03e1afc..3e7d6b030 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/DecodeFrame.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/DecodeFrame.cs @@ -1,4 +1,4 @@ -using Ryujinx.Common.Memory; +using Ryujinx.Common.Memory; using Ryujinx.Graphics.Nvdec.Vp9.Common; using Ryujinx.Graphics.Nvdec.Vp9.Dsp; using Ryujinx.Graphics.Nvdec.Vp9.Types; @@ -12,14 +12,96 @@ using System.Threading.Tasks; namespace Ryujinx.Graphics.Nvdec.Vp9 { - static class DecodeFrame + internal static class DecodeFrame { private static bool ReadIsValid(ArrayPtr start, int len) { return len != 0 && len <= start.Length; } - private static void InverseTransformBlockInter(ref MacroBlockD xd, int plane, TxSize txSize, Span dst, int stride, int eob) + private static void ReadTxModeProbs(ref Vp9EntropyProbs txProbs, ref Reader r) + { + for (int i = 0; i < EntropyMode.TxSizeContexts; ++i) + { + for (int j = 0; j < (int)TxSize.TxSizes - 3; ++j) + { + r.DiffUpdateProb(ref txProbs.Tx8x8Prob[i][j]); + } + } + + for (int i = 0; i < EntropyMode.TxSizeContexts; ++i) + { + for (int j = 0; j < (int)TxSize.TxSizes - 2; ++j) + { + r.DiffUpdateProb(ref txProbs.Tx16x16Prob[i][j]); + } + } + + for (int i = 0; i < EntropyMode.TxSizeContexts; ++i) + { + for (int j = 0; j < (int)TxSize.TxSizes - 1; ++j) + { + r.DiffUpdateProb(ref txProbs.Tx32x32Prob[i][j]); + } + } + } + + private static void ReadSwitchableInterpProbs(ref Vp9EntropyProbs fc, ref Reader r) + { + for (int j = 0; j < Constants.SwitchableFilterContexts; ++j) + { + for (int i = 0; i < Constants.SwitchableFilters - 1; ++i) + { + r.DiffUpdateProb(ref fc.SwitchableInterpProb[j][i]); + } + } + } + + private static void ReadInterModeProbs(ref Vp9EntropyProbs fc, ref Reader r) + { + for (int i = 0; i < Constants.InterModeContexts; ++i) + { + for (int j = 0; j < Constants.InterModes - 1; ++j) + { + r.DiffUpdateProb( ref fc.InterModeProb[i][j]); + } + } + } + + private static void ReadMvProbs(ref Vp9EntropyProbs ctx, bool allowHp, ref Reader r) + { + r.UpdateMvProbs(ctx.Joints.AsSpan(), EntropyMv.Joints - 1); + + for (int i = 0; i < 2; ++i) + { + r.UpdateMvProbs(MemoryMarshal.CreateSpan(ref ctx.Sign[i], 1), 1); + r.UpdateMvProbs(ctx.Classes[i].AsSpan(), EntropyMv.Classes - 1); + r.UpdateMvProbs(ctx.Class0[i].AsSpan(), EntropyMv.Class0Size - 1); + r.UpdateMvProbs(ctx.Bits[i].AsSpan(), EntropyMv.OffsetBits); + } + + for (int i = 0; i < 2; ++i) + { + for (int j = 0; j < EntropyMv.Class0Size; ++j) + { + r.UpdateMvProbs(ctx.Class0Fp[i][j].AsSpan(), EntropyMv.FpSize - 1); + } + + r.UpdateMvProbs(ctx.Fp[i].AsSpan(), 3); + } + + if (allowHp) + { + for (int i = 0; i < 2; ++i) + { + r.UpdateMvProbs(MemoryMarshal.CreateSpan(ref ctx.Class0Hp[i], 1), 1); + r.UpdateMvProbs(MemoryMarshal.CreateSpan(ref ctx.Hp[i], 1), 1); + } + } + } + + private static void InverseTransformBlockInter(ref MacroBlockD xd, int plane, TxSize txSize, Span dst, + int stride, int eob) { ref MacroBlockDPlane pd = ref xd.Plane[plane]; ArrayPtr dqcoeff = pd.DqCoeff; @@ -29,23 +111,23 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 Span dst16 = MemoryMarshal.Cast(dst); if (xd.Lossless) { - Idct.HighbdIwht4x4Add(dqcoeff.AsSpan(), dst16, stride, eob, xd.Bd); + Idct.HighbdIwht4X4Add(dqcoeff.AsSpan(), dst16, stride, eob, xd.Bd); } else { switch (txSize) { - case TxSize.Tx4x4: - Idct.HighbdIdct4x4Add(dqcoeff.AsSpan(), dst16, stride, eob, xd.Bd); + case TxSize.Tx4X4: + Idct.HighbdIdct4X4Add(dqcoeff.AsSpan(), dst16, stride, eob, xd.Bd); break; - case TxSize.Tx8x8: - Idct.HighbdIdct8x8Add(dqcoeff.AsSpan(), dst16, stride, eob, xd.Bd); + case TxSize.Tx8X8: + Idct.HighbdIdct8X8Add(dqcoeff.AsSpan(), dst16, stride, eob, xd.Bd); break; - case TxSize.Tx16x16: - Idct.HighbdIdct16x16Add(dqcoeff.AsSpan(), dst16, stride, eob, xd.Bd); + case TxSize.Tx16X16: + Idct.HighbdIdct16X16Add(dqcoeff.AsSpan(), dst16, stride, eob, xd.Bd); break; - case TxSize.Tx32x32: - Idct.HighbdIdct32x32Add(dqcoeff.AsSpan(), dst16, stride, eob, xd.Bd); + case TxSize.Tx32X32: + Idct.HighbdIdct32X32Add(dqcoeff.AsSpan(), dst16, stride, eob, xd.Bd); break; default: Debug.Assert(false, "Invalid transform size"); @@ -57,23 +139,23 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { if (xd.Lossless) { - Idct.Iwht4x4Add(dqcoeff.AsSpan(), dst, stride, eob); + Idct.Iwht4X4Add(dqcoeff.AsSpan(), dst, stride, eob); } else { switch (txSize) { - case TxSize.Tx4x4: - Idct.Idct4x4Add(dqcoeff.AsSpan(), dst, stride, eob); + case TxSize.Tx4X4: + Idct.Idct4X4Add(dqcoeff.AsSpan(), dst, stride, eob); break; - case TxSize.Tx8x8: - Idct.Idct8x8Add(dqcoeff.AsSpan(), dst, stride, eob); + case TxSize.Tx8X8: + Idct.Idct8X8Add(dqcoeff.AsSpan(), dst, stride, eob); break; - case TxSize.Tx16x16: - Idct.Idct16x16Add(dqcoeff.AsSpan(), dst, stride, eob); + case TxSize.Tx16X16: + Idct.Idct16X16Add(dqcoeff.AsSpan(), dst, stride, eob); break; - case TxSize.Tx32x32: - Idct.Idct32x32Add(dqcoeff.AsSpan(), dst, stride, eob); + case TxSize.Tx32X32: + Idct.Idct32X32Add(dqcoeff.AsSpan(), dst, stride, eob); break; default: Debug.Assert(false, "Invalid transform size"); @@ -88,17 +170,17 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 } else { - if (txSize <= TxSize.Tx16x16 && eob <= 10) + if (txSize <= TxSize.Tx16X16 && eob <= 10) { - dqcoeff.AsSpan()[..(4 * (4 << (int)txSize))].Clear(); + dqcoeff.AsSpan().Slice(0, 4 * (4 << (int)txSize)).Clear(); } - else if (txSize == TxSize.Tx32x32 && eob <= 34) + else if (txSize == TxSize.Tx32X32 && eob <= 34) { - dqcoeff.AsSpan()[..256].Clear(); + dqcoeff.AsSpan().Slice(0, 256).Clear(); } else { - dqcoeff.AsSpan()[..(16 << ((int)txSize << 1))].Clear(); + dqcoeff.AsSpan().Slice(0, 16 << ((int)txSize << 1)).Clear(); } } } @@ -120,23 +202,23 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 Span dst16 = MemoryMarshal.Cast(dst); if (xd.Lossless) { - Idct.HighbdIwht4x4Add(dqcoeff.AsSpan(), dst16, stride, eob, xd.Bd); + Idct.HighbdIwht4X4Add(dqcoeff.AsSpan(), dst16, stride, eob, xd.Bd); } else { switch (txSize) { - case TxSize.Tx4x4: - Idct.HighbdIht4x4Add(txType, dqcoeff.AsSpan(), dst16, stride, eob, xd.Bd); + case TxSize.Tx4X4: + Idct.HighbdIht4X4Add(txType, dqcoeff.AsSpan(), dst16, stride, eob, xd.Bd); break; - case TxSize.Tx8x8: - Idct.HighbdIht8x8Add(txType, dqcoeff.AsSpan(), dst16, stride, eob, xd.Bd); + case TxSize.Tx8X8: + Idct.HighbdIht8X8Add(txType, dqcoeff.AsSpan(), dst16, stride, eob, xd.Bd); break; - case TxSize.Tx16x16: - Idct.HighbdIht16x16Add(txType, dqcoeff.AsSpan(), dst16, stride, eob, xd.Bd); + case TxSize.Tx16X16: + Idct.HighbdIht16X16Add(txType, dqcoeff.AsSpan(), dst16, stride, eob, xd.Bd); break; - case TxSize.Tx32x32: - Idct.HighbdIdct32x32Add(dqcoeff.AsSpan(), dst16, stride, eob, xd.Bd); + case TxSize.Tx32X32: + Idct.HighbdIdct32X32Add(dqcoeff.AsSpan(), dst16, stride, eob, xd.Bd); break; default: Debug.Assert(false, "Invalid transform size"); @@ -148,23 +230,23 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { if (xd.Lossless) { - Idct.Iwht4x4Add(dqcoeff.AsSpan(), dst, stride, eob); + Idct.Iwht4X4Add(dqcoeff.AsSpan(), dst, stride, eob); } else { switch (txSize) { - case TxSize.Tx4x4: - Idct.Iht4x4Add(txType, dqcoeff.AsSpan(), dst, stride, eob); + case TxSize.Tx4X4: + Idct.Iht4X4Add(txType, dqcoeff.AsSpan(), dst, stride, eob); break; - case TxSize.Tx8x8: - Idct.Iht8x8Add(txType, dqcoeff.AsSpan(), dst, stride, eob); + case TxSize.Tx8X8: + Idct.Iht8X8Add(txType, dqcoeff.AsSpan(), dst, stride, eob); break; - case TxSize.Tx16x16: - Idct.Iht16x16Add(txType, dqcoeff.AsSpan(), dst, stride, eob); + case TxSize.Tx16X16: + Idct.Iht16X16Add(txType, dqcoeff.AsSpan(), dst, stride, eob); break; - case TxSize.Tx32x32: - Idct.Idct32x32Add(dqcoeff.AsSpan(), dst, stride, eob); + case TxSize.Tx32X32: + Idct.Idct32X32Add(dqcoeff.AsSpan(), dst, stride, eob); break; default: Debug.Assert(false, "Invalid transform size"); @@ -179,17 +261,17 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 } else { - if (txType == TxType.DctDct && txSize <= TxSize.Tx16x16 && eob <= 10) + if (txType == TxType.DctDct && txSize <= TxSize.Tx16X16 && eob <= 10) { - dqcoeff.AsSpan()[..(4 * (4 << (int)txSize))].Clear(); + dqcoeff.AsSpan().Slice(0, 4 * (4 << (int)txSize)).Clear(); } - else if (txSize == TxSize.Tx32x32 && eob <= 34) + else if (txSize == TxSize.Tx32X32 && eob <= 34) { - dqcoeff.AsSpan()[..256].Clear(); + dqcoeff.AsSpan().Slice(0, 256).Clear(); } else { - dqcoeff.AsSpan()[..(16 << ((int)txSize << 1))].Clear(); + dqcoeff.AsSpan().Slice(0, 16 << ((int)txSize << 1)).Clear(); } } } @@ -204,12 +286,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { ref MacroBlockD xd = ref twd.Xd; ref MacroBlockDPlane pd = ref xd.Plane[plane]; - PredictionMode mode = (plane == 0) ? mi.Mode : mi.UvMode; - int dstOffset = 4 * row * pd.Dst.Stride + 4 * col; + PredictionMode mode = plane == 0 ? mi.Mode : mi.UvMode; + int dstOffset = (4 * row * pd.Dst.Stride) + (4 * col); byte* dst = &pd.Dst.Buf.ToPointer()[dstOffset]; - Span dstSpan = pd.Dst.Buf.AsSpan()[dstOffset..]; + Span dstSpan = pd.Dst.Buf.AsSpan().Slice(dstOffset); - if (mi.SbType < BlockSize.Block8x8) + if (mi.SbType < BlockSize.Block8X8) { if (plane == 0) { @@ -217,15 +299,16 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 } } - ReconIntra.PredictIntraBlock(ref xd, pd.N4Wl, txSize, mode, dst, pd.Dst.Stride, dst, pd.Dst.Stride, col, row, plane); + ReconIntra.PredictIntraBlock(ref xd, pd.N4Wl, txSize, mode, dst, pd.Dst.Stride, dst, pd.Dst.Stride, col, + row, plane); if (mi.Skip == 0) { TxType txType = - (plane != 0 || xd.Lossless) ? TxType.DctDct : ReconIntra.IntraModeToTxTypeLookup[(int)mode]; - var sc = (plane != 0 || xd.Lossless) - ? Luts.Vp9DefaultScanOrders[(int)txSize] - : Luts.Vp9ScanOrders[(int)txSize][(int)txType]; + plane != 0 || xd.Lossless ? TxType.DctDct : ReconIntra.IntraModeToTxTypeLookup[(int)mode]; + Luts.ScanOrder sc = plane != 0 || xd.Lossless + ? Luts.DefaultScanOrders[(int)txSize] + : Luts.ScanOrders[(int)txSize][(int)txType]; int eob = Detokenize.DecodeBlockTokens(ref twd, plane, sc, col, row, txSize, mi.SegmentId); if (eob > 0) { @@ -244,14 +327,15 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { ref MacroBlockD xd = ref twd.Xd; ref MacroBlockDPlane pd = ref xd.Plane[plane]; - var sc = Luts.Vp9DefaultScanOrders[(int)txSize]; + Luts.ScanOrder sc = Luts.DefaultScanOrders[(int)txSize]; int eob = Detokenize.DecodeBlockTokens(ref twd, plane, sc, col, row, txSize, mi.SegmentId); - Span dst = pd.Dst.Buf.AsSpan()[(4 * row * pd.Dst.Stride + 4 * col)..]; + Span dst = pd.Dst.Buf.AsSpan().Slice((4 * row * pd.Dst.Stride) + (4 * col)); if (eob > 0) { InverseTransformBlockInter(ref xd, plane, txSize, dst, pd.Dst.Stride, eob); } + return eob; } @@ -268,7 +352,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 int h) { // Get a pointer to the start of the real data for this row. - byte* refRow = src - x - y * srcStride; + byte* refRow = src - x - (y * srcStride); if (y >= h) { @@ -340,7 +424,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { // Get a pointer to the start of the real data for this row. ushort* src = (ushort*)src8; - ushort* refRow = src - x - y * srcStride; + ushort* refRow = src - x - (y * srcStride); if (y >= h) { @@ -483,9 +567,9 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 int refr) { ref MacroBlockDPlane pd = ref xd.Plane[plane]; - byte* dst = dstBuf.Buf.ToPointer() + dstBuf.Stride * y + x; + byte* dst = dstBuf.Buf.ToPointer() + (dstBuf.Stride * y) + x; Mv32 scaledMv; - int xs, ys, x0, y0, x0_16, y0_16, frameWidth, frameHeight, bufStride, subpelX, subpelY; + int xs, ys, x0, y0, x016, y016, frameWidth, frameHeight, bufStride, subpelX, subpelY; byte* refFrame; byte* bufPtr; @@ -507,16 +591,16 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { Mv mvQ4 = ReconInter.ClampMvToUmvBorderSb(ref xd, ref mv, bw, bh, pd.SubsamplingX, pd.SubsamplingY); // Co-ordinate of containing block to pixel precision. - int xStart = (-xd.MbToLeftEdge >> (3 + pd.SubsamplingX)); - int yStart = (-xd.MbToTopEdge >> (3 + pd.SubsamplingY)); + int xStart = -xd.MbToLeftEdge >> (3 + pd.SubsamplingX); + int yStart = -xd.MbToTopEdge >> (3 + pd.SubsamplingY); // Co-ordinate of the block to 1/16th pixel precision. - x0_16 = (xStart + x) << Filter.SubpelBits; - y0_16 = (yStart + y) << Filter.SubpelBits; + x016 = (xStart + x) << Filter.SubpelBits; + y016 = (yStart + y) << Filter.SubpelBits; // Co-ordinate of current block in reference frame // to 1/16th pixel precision. - x0_16 = sf.ScaleValueX(x0_16); - y0_16 = sf.ScaleValueY(y0_16); + x016 = sf.ScaleValueX(x016); + y016 = sf.ScaleValueY(y016); // Map the top left corner of the block into the reference frame. x0 = sf.ScaleValueX(xStart + x); @@ -535,13 +619,14 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 y0 = (-xd.MbToTopEdge >> (3 + pd.SubsamplingY)) + y; // Co-ordinate of the block to 1/16th pixel precision. - x0_16 = x0 << Filter.SubpelBits; - y0_16 = y0 << Filter.SubpelBits; + x016 = x0 << Filter.SubpelBits; + y016 = y0 << Filter.SubpelBits; scaledMv.Row = mv.Row * (1 << (1 - pd.SubsamplingY)); scaledMv.Col = mv.Col * (1 << (1 - pd.SubsamplingX)); xs = ys = 16; } + subpelX = scaledMv.Col & Filter.SubpelMask; subpelY = scaledMv.Row & Filter.SubpelMask; @@ -549,34 +634,35 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 // reference frame. x0 += scaledMv.Col >> Filter.SubpelBits; y0 += scaledMv.Row >> Filter.SubpelBits; - x0_16 += scaledMv.Col; - y0_16 += scaledMv.Row; + x016 += scaledMv.Col; + y016 += scaledMv.Row; // Get reference block pointer. - bufPtr = refFrame + y0 * preBuf.Stride + x0; + bufPtr = refFrame + (y0 * preBuf.Stride) + x0; bufStride = preBuf.Stride; // Do border extension if there is motion or the // width/height is not a multiple of 8 pixels. - if (isScaled || scaledMv.Col != 0 || scaledMv.Row != 0 || (frameWidth & 0x7) != 0 || (frameHeight & 0x7) != 0) + if (isScaled || scaledMv.Col != 0 || scaledMv.Row != 0 || (frameWidth & 0x7) != 0 || + (frameHeight & 0x7) != 0) { - int y1 = ((y0_16 + (h - 1) * ys) >> Filter.SubpelBits) + 1; + int y1 = ((y016 + ((h - 1) * ys)) >> Filter.SubpelBits) + 1; // Get reference block bottom right horizontal coordinate. - int x1 = ((x0_16 + (w - 1) * xs) >> Filter.SubpelBits) + 1; + int x1 = ((x016 + ((w - 1) * xs)) >> Filter.SubpelBits) + 1; int xPad = 0, yPad = 0; - if (subpelX != 0 || (sf.XStepQ4 != Filter.SubpelShifts)) + if (subpelX != 0 || sf.XStepQ4 != Filter.SubpelShifts) { - x0 -= Constants.Vp9InterpExtend - 1; - x1 += Constants.Vp9InterpExtend; + x0 -= Constants.InterpExtend - 1; + x1 += Constants.InterpExtend; xPad = 1; } - if (subpelY != 0 || (sf.YStepQ4 != Filter.SubpelShifts)) + if (subpelY != 0 || sf.YStepQ4 != Filter.SubpelShifts) { - y0 -= Constants.Vp9InterpExtend - 1; - y1 += Constants.Vp9InterpExtend; + y0 -= Constants.InterpExtend - 1; + y1 += Constants.InterpExtend; yPad = 1; } @@ -585,10 +671,10 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 y0 < 0 || y0 > frameHeight - 1 || y1 < 0 || y1 > frameHeight - 1) { // Extend the border. - byte* bufPtr1 = refFrame + y0 * bufStride + x0; + byte* bufPtr1 = refFrame + (y0 * bufStride) + x0; int bW = x1 - x0 + 1; int bH = y1 - y0 + 1; - int borderOffset = yPad * 3 * bW + xPad * 3; + int borderOffset = (yPad * 3 * bW) + (xPad * 3); ExtendAndPredict( bufPtr1, @@ -612,7 +698,6 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 refr, xs, ys); - return; } } @@ -660,7 +745,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 int miX = miCol * Constants.MiSize; int miY = miRow * Constants.MiSize; ref ModeInfo mi = ref xd.Mi[0].Value; - Array8[] kernel = Luts.Vp9FilterKernels[mi.InterpFilter]; + Array8[] kernel = Luts.FilterKernels[mi.InterpFilter]; BlockSize sbType = mi.SbType; int isCompound = mi.HasSecondRef() ? 1 : 0; int refr; @@ -675,28 +760,30 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 if (!sf.IsValidScale()) { - xd.ErrorInfo.Value.InternalError(CodecErr.CodecUnsupBitstream, "Reference frame has invalid dimensions"); + xd.ErrorInfo.Value.InternalError(CodecErr.UnsupBitstream, + "Reference frame has invalid dimensions"); } isScaled = sf.IsScaled(); - ReconInter.SetupPrePlanes(ref xd, refr, ref refFrameBuf, miRow, miCol, isScaled ? new Ptr(ref sf) : Ptr.Null); + ReconInter.SetupPrePlanes(ref xd, refr, ref refFrameBuf, miRow, miCol, + isScaled ? new Ptr(ref sf) : Ptr.Null); xd.BlockRefs[refr] = new Ptr(ref refBuf); - if (sbType < BlockSize.Block8x8) + if (sbType < BlockSize.Block8X8) { for (plane = 0; plane < Constants.MaxMbPlane; ++plane) { ref MacroBlockDPlane pd = ref xd.Plane[plane]; ref Buf2D dstBuf = ref pd.Dst; - int num4x4W = pd.N4W; - int num4x4H = pd.N4H; - int n4Wx4 = 4 * num4x4W; - int n4Hx4 = 4 * num4x4H; + int num4X4W = pd.N4W; + int num4X4H = pd.N4H; + int n4Wx4 = 4 * num4X4W; + int n4Hx4 = 4 * num4X4H; ref Buf2D preBuf = ref pd.Pre[refr]; - int i = 0, x, y; - for (y = 0; y < num4x4H; ++y) + int i = 0; + for (int y = 0; y < num4X4H; ++y) { - for (x = 0; x < num4x4W; ++x) + for (int x = 0; x < num4X4W; ++x) { Mv mv = ReconInter.AverageSplitMvs(ref pd, ref mi, refr, i++); DecBuildInterPredictors( @@ -729,10 +816,10 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { ref MacroBlockDPlane pd = ref xd.Plane[plane]; ref Buf2D dstBuf = ref pd.Dst; - int num4x4W = pd.N4W; - int num4x4H = pd.N4H; - int n4Wx4 = 4 * num4x4W; - int n4Hx4 = 4 * num4x4H; + int num4X4W = pd.N4W; + int num4X4H = pd.N4H; + int n4Wx4 = 4 * num4X4W; + int n4Hx4 = 4 * num4X4H; ref Buf2D preBuf = ref pd.Pre[refr]; DecBuildInterPredictors( ref xd, @@ -758,21 +845,9 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 } } - private static unsafe void DecResetSkipContext(ref MacroBlockD xd) - { - int i; - for (i = 0; i < Constants.MaxMbPlane; i++) - { - ref MacroBlockDPlane pd = ref xd.Plane[i]; - MemoryUtil.Fill(pd.AboveContext.ToPointer(), (sbyte)0, pd.N4W); - MemoryUtil.Fill(pd.LeftContext.ToPointer(), (sbyte)0, pd.N4H); - } - } - private static void SetPlaneN4(ref MacroBlockD xd, int bw, int bh, int bwl, int bhl) { - int i; - for (i = 0; i < Constants.MaxMbPlane; i++) + for (int i = 0; i < Constants.MaxMbPlane; i++) { xd.Plane[i].N4W = (ushort)((bw << 1) >> xd.Plane[i].SubsamplingX); xd.Plane[i].N4H = (ushort)((bh << 1) >> xd.Plane[i].SubsamplingY); @@ -794,18 +869,18 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 int bwl, int bhl) { - int offset = miRow * cm.MiStride + miCol; - int x, y; + int offset = (miRow * cm.MiStride) + miCol; + ref TileInfo tile = ref xd.Tile; xd.Mi = cm.MiGridVisible.Slice(offset); xd.Mi[0] = new Ptr(ref cm.Mi[offset]); xd.Mi[0].Value.SbType = bsize; - for (y = 0; y < yMis; ++y) + for (int y = 0; y < yMis; ++y) { - for (x = y == 0 ? 1 : 0; x < xMis; ++x) + for (int x = y == 0 ? 1 : 0; x < xMis; ++x) { - xd.Mi[y * cm.MiStride + x] = xd.Mi[0]; + xd.Mi[(y * cm.MiStride) + x] = xd.Mi[0]; } } @@ -818,7 +893,6 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 xd.SetMiRowCol(ref tile, miRow, bh, miCol, bw, cm.MiRows, cm.MiCols); ReconInter.SetupDstPlanes(ref xd.Plane, ref xd.CurBuf, miRow, miCol); - return ref xd.Mi[0].Value; } @@ -831,7 +905,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 int bwl, int bhl) { - bool less8x8 = bsize < BlockSize.Block8x8; + bool less8X8 = bsize < BlockSize.Block8X8; int bw = 1 << (bwl - 1); int bh = 1 << (bhl - 1); int xMis = Math.Min(bw, cm.MiCols - miCol); @@ -841,12 +915,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 ref ModeInfo mi = ref SetOffsets(ref cm, ref xd, bsize, miRow, miCol, bw, bh, xMis, yMis, bwl, bhl); - if (bsize >= BlockSize.Block8x8 && (cm.SubsamplingX != 0 || cm.SubsamplingY != 0)) + if (bsize >= BlockSize.Block8X8 && (cm.SubsamplingX != 0 || cm.SubsamplingY != 0)) { BlockSize uvSubsize = Luts.SsSizeLookup[(int)bsize][cm.SubsamplingX][cm.SubsamplingY]; if (uvSubsize == BlockSize.BlockInvalid) { - xd.ErrorInfo.Value.InternalError(CodecErr.CodecCorruptFrame, "Invalid block size."); + xd.ErrorInfo.Value.InternalError(CodecErr.CorruptFrame, "Invalid block size."); } } @@ -854,7 +928,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 if (mi.Skip != 0) { - DecResetSkipContext(ref xd); + xd.DecResetSkipContext(); } if (!mi.IsInterBlock()) @@ -864,12 +938,14 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { ref MacroBlockDPlane pd = ref xd.Plane[plane]; TxSize txSize = plane != 0 ? mi.GetUvTxSize(ref pd) : mi.TxSize; - int num4x4W = pd.N4W; - int num4x4H = pd.N4H; + int num4X4W = pd.N4W; + int num4X4H = pd.N4H; int step = 1 << (int)txSize; int row, col; - int maxBlocksWide = num4x4W + (xd.MbToRightEdge >= 0 ? 0 : xd.MbToRightEdge >> (5 + pd.SubsamplingX)); - int maxBlocksHigh = num4x4H + (xd.MbToBottomEdge >= 0 ? 0 : xd.MbToBottomEdge >> (5 + pd.SubsamplingY)); + int maxBlocksWide = + num4X4W + (xd.MbToRightEdge >= 0 ? 0 : xd.MbToRightEdge >> (5 + pd.SubsamplingX)); + int maxBlocksHigh = + num4X4H + (xd.MbToBottomEdge >= 0 ? 0 : xd.MbToBottomEdge >> (5 + pd.SubsamplingY)); xd.MaxBlocksWide = (uint)(xd.MbToRightEdge >= 0 ? 0 : maxBlocksWide); xd.MaxBlocksHigh = (uint)(xd.MbToBottomEdge >= 0 ? 0 : maxBlocksHigh); @@ -898,12 +974,14 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { ref MacroBlockDPlane pd = ref xd.Plane[plane]; TxSize txSize = plane != 0 ? mi.GetUvTxSize(ref pd) : mi.TxSize; - int num4x4W = pd.N4W; - int num4x4H = pd.N4H; + int num4X4W = pd.N4W; + int num4X4H = pd.N4H; int step = 1 << (int)txSize; int row, col; - int maxBlocksWide = num4x4W + (xd.MbToRightEdge >= 0 ? 0 : xd.MbToRightEdge >> (5 + pd.SubsamplingX)); - int maxBlocksHigh = num4x4H + (xd.MbToBottomEdge >= 0 ? 0 : xd.MbToBottomEdge >> (5 + pd.SubsamplingY)); + int maxBlocksWide = + num4X4W + (xd.MbToRightEdge >= 0 ? 0 : xd.MbToRightEdge >> (5 + pd.SubsamplingX)); + int maxBlocksHigh = num4X4H + + (xd.MbToBottomEdge >= 0 ? 0 : xd.MbToBottomEdge >> (5 + pd.SubsamplingY)); xd.MaxBlocksWide = (uint)(xd.MbToRightEdge >= 0 ? 0 : maxBlocksWide); xd.MaxBlocksHigh = (uint)(xd.MbToBottomEdge >= 0 ? 0 : maxBlocksHigh); @@ -917,7 +995,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 } } - if (!less8x8 && eobtotal == 0) + if (!less8X8 && eobtotal == 0) { mi.Skip = 1; // Skip loopfilter } @@ -932,15 +1010,6 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 } } - private static int DecPartitionPlaneContext(ref TileWorkerData twd, int miRow, int miCol, int bsl) - { - ref sbyte aboveCtx = ref twd.Xd.AboveSegContext[miCol]; - ref sbyte leftCtx = ref twd.Xd.LeftSegContext[miRow & Constants.MiMask]; - int above = (aboveCtx >> bsl) & 1, left = (leftCtx >> bsl) & 1; - - return (left * 2 + above) + bsl * Constants.PartitionPloffset; - } - private static void DecUpdatePartitionContext( ref TileWorkerData twd, int miRow, @@ -949,13 +1018,14 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 int bw) { Span aboveCtx = twd.Xd.AboveSegContext.Slice(miCol).AsSpan(); - Span leftCtx = MemoryMarshal.CreateSpan(ref twd.Xd.LeftSegContext[miRow & Constants.MiMask], 8 - (miRow & Constants.MiMask)); + Span leftCtx = MemoryMarshal.CreateSpan(ref twd.Xd.LeftSegContext[miRow & Constants.MiMask], + 8 - (miRow & Constants.MiMask)); // Update the partition context at the end notes. Set partition bits // of block sizes larger than the current one to be one, and partition // bits of smaller block sizes to be zero. - aboveCtx[..bw].Fill(Luts.PartitionContextLookup[(int)subsize].Above); - leftCtx[..bw].Fill(Luts.PartitionContextLookup[(int)subsize].Left); + aboveCtx.Slice(0, bw).Fill(Luts.PartitionContextLookup[(int)subsize].Above); + leftCtx.Slice(0, bw).Fill(Luts.PartitionContextLookup[(int)subsize].Left); } private static PartitionType ReadPartition( @@ -966,14 +1036,14 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 int hasCols, int bsl) { - int ctx = DecPartitionPlaneContext(ref twd, miRow, miCol, bsl); + int ctx = twd.DecPartitionPlaneContext(miRow, miCol, bsl); ReadOnlySpan probs = MemoryMarshal.CreateReadOnlySpan(ref twd.Xd.PartitionProbs[ctx][0], 3); PartitionType p; ref Reader r = ref twd.BitReader; if (hasRows != 0 && hasCols != 0) { - p = (PartitionType)r.ReadTree(Luts.Vp9PartitionTree, probs); + p = (PartitionType)r.ReadTree(Luts.PartitionTree, probs); } else if (hasRows == 0 && hasCols != 0) { @@ -1002,15 +1072,15 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 int miRow, int miCol, BlockSize bsize, - int n4x4L2) + int n4X4L2) { - int n8x8L2 = n4x4L2 - 1; - int num8x8Wh = 1 << n8x8L2; - int hbs = num8x8Wh >> 1; + int n8X8L2 = n4X4L2 - 1; + int num8X8Wh = 1 << n8X8L2; + int hbs = num8X8Wh >> 1; PartitionType partition; BlockSize subsize; - bool hasRows = (miRow + hbs) < cm.MiRows; - bool hasCols = (miCol + hbs) < cm.MiCols; + bool hasRows = miRow + hbs < cm.MiRows; + bool hasCols = miCol + hbs < cm.MiCols; ref MacroBlockD xd = ref twd.Xd; if (miRow >= cm.MiRows || miCol >= cm.MiCols) @@ -1018,7 +1088,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 return; } - partition = ReadPartition(ref twd, miRow, miCol, hasRows ? 1 : 0, hasCols ? 1 : 0, n8x8L2); + partition = ReadPartition(ref twd, miRow, miCol, hasRows ? 1 : 0, hasCols ? 1 : 0, n8X8L2); subsize = Luts.SubsizeLookup[(int)partition][(int)bsize]; if (hbs == 0) { @@ -1032,29 +1102,29 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 switch (partition) { case PartitionType.PartitionNone: - DecodeBlock(ref twd, ref cm, miRow, miCol, subsize, n4x4L2, n4x4L2); + DecodeBlock(ref twd, ref cm, miRow, miCol, subsize, n4X4L2, n4X4L2); break; case PartitionType.PartitionHorz: - DecodeBlock(ref twd, ref cm, miRow, miCol, subsize, n4x4L2, n8x8L2); + DecodeBlock(ref twd, ref cm, miRow, miCol, subsize, n4X4L2, n8X8L2); if (hasRows) { - DecodeBlock(ref twd, ref cm, miRow + hbs, miCol, subsize, n4x4L2, n8x8L2); + DecodeBlock(ref twd, ref cm, miRow + hbs, miCol, subsize, n4X4L2, n8X8L2); } break; case PartitionType.PartitionVert: - DecodeBlock(ref twd, ref cm, miRow, miCol, subsize, n8x8L2, n4x4L2); + DecodeBlock(ref twd, ref cm, miRow, miCol, subsize, n8X8L2, n4X4L2); if (hasCols) { - DecodeBlock(ref twd, ref cm, miRow, miCol + hbs, subsize, n8x8L2, n4x4L2); + DecodeBlock(ref twd, ref cm, miRow, miCol + hbs, subsize, n8X8L2, n4X4L2); } break; case PartitionType.PartitionSplit: - DecodePartition(ref twd, ref cm, miRow, miCol, subsize, n8x8L2); - DecodePartition(ref twd, ref cm, miRow, miCol + hbs, subsize, n8x8L2); - DecodePartition(ref twd, ref cm, miRow + hbs, miCol, subsize, n8x8L2); - DecodePartition(ref twd, ref cm, miRow + hbs, miCol + hbs, subsize, n8x8L2); + DecodePartition(ref twd, ref cm, miRow, miCol, subsize, n8X8L2); + DecodePartition(ref twd, ref cm, miRow, miCol + hbs, subsize, n8X8L2); + DecodePartition(ref twd, ref cm, miRow + hbs, miCol, subsize, n8X8L2); + DecodePartition(ref twd, ref cm, miRow + hbs, miCol + hbs, subsize, n8X8L2); break; default: Debug.Assert(false, "Invalid partition type"); @@ -1063,9 +1133,10 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 } // Update partition context - if (bsize >= BlockSize.Block8x8 && (bsize == BlockSize.Block8x8 || partition != PartitionType.PartitionSplit)) + if (bsize >= BlockSize.Block8X8 && + (bsize == BlockSize.Block8X8 || partition != PartitionType.PartitionSplit)) { - DecUpdatePartitionContext(ref twd, miRow, miCol, subsize, num8x8Wh); + DecUpdatePartitionContext(ref twd, miRow, miCol, subsize, num8X8Wh); } } @@ -1079,15 +1150,257 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 // partition can't be fully read then throw an error. if (!ReadIsValid(data, readSize)) { - errorInfo.InternalError(CodecErr.CodecCorruptFrame, "Truncated packet or corrupt tile length"); + errorInfo.InternalError(CodecErr.CorruptFrame, "Truncated packet or corrupt tile length"); } if (r.Init(data, readSize)) { - errorInfo.InternalError(CodecErr.CodecMemError, "Failed to allocate bool decoder 1"); + errorInfo.InternalError(CodecErr.MemError, "Failed to allocate bool decoder 1"); } } + private static void ReadCoefProbsCommon(ref Array2>>>> coefProbs, + ref Reader r, int txSize) + { + if (r.ReadBit() != 0) + { + for (int i = 0; i < Constants.PlaneTypes; ++i) + { + for (int j = 0; j < Entropy.RefTypes; ++j) + { + for (int k = 0; k < Entropy.CoefBands; ++k) + { + for (int l = 0; l < Entropy.BAND_COEFF_CONTEXTS(k); ++l) + { + for (int m = 0; m < Entropy.UnconstrainedNodes; ++m) + { + r.DiffUpdateProb( ref coefProbs[i][j][k][l][m]); + } + } + } + } + } + } + } + + private static void ReadCoefProbs(ref Vp9EntropyProbs fc, TxMode txMode, ref Reader r) + { + int maxTxSize = (int)Luts.TxModeToBiggestTxSize[(int)txMode]; + for (int txSize = (int)TxSize.Tx4X4; txSize <= maxTxSize; ++txSize) + { + ReadCoefProbsCommon(ref fc.CoefProbs[txSize], ref r, txSize); + } + } + + private static void SetupLoopfilter(ref Types.LoopFilter lf, ref ReadBitBuffer rb) + { + lf.FilterLevel = rb.ReadLiteral(6); + lf.SharpnessLevel = rb.ReadLiteral(3); + + // Read in loop filter deltas applied at the MB level based on mode or ref + // frame. + lf.ModeRefDeltaUpdate = false; + + lf.ModeRefDeltaEnabled = rb.ReadBit() != 0; + if (lf.ModeRefDeltaEnabled) + { + lf.ModeRefDeltaUpdate = rb.ReadBit() != 0; + if (lf.ModeRefDeltaUpdate) + { + for (int i = 0; i < LoopFilter.MaxRefLfDeltas; i++) + { + if (rb.ReadBit() != 0) + { + lf.RefDeltas[i] = (sbyte)rb.ReadSignedLiteral(6); + } + } + + for (int i = 0; i < LoopFilter.MaxModeLfDeltas; i++) + { + if (rb.ReadBit() != 0) + { + lf.ModeDeltas[i] = (sbyte)rb.ReadSignedLiteral(6); + } + } + } + } + } + + private static void SetupQuantization(ref Vp9Common cm, ref MacroBlockD xd, ref ReadBitBuffer rb) + { + cm.BaseQindex = rb.ReadLiteral(QuantCommon.QindexBits); + cm.YDcDeltaQ = rb.ReadDeltaQ(); + cm.UvDcDeltaQ = rb.ReadDeltaQ(); + cm.UvAcDeltaQ = rb.ReadDeltaQ(); + cm.DequantBitDepth = cm.BitDepth; + xd.Lossless = cm.BaseQindex == 0 && cm.YDcDeltaQ == 0 && cm.UvDcDeltaQ == 0 && cm.UvAcDeltaQ == 0; + + xd.Bd = (int)cm.BitDepth; + } + + private static readonly byte[] _literalToFilter = + [ + Constants.EightTapSmooth, Constants.EightTap, Constants.EightTapSharp, Constants.Bilinear + ]; + + private static byte ReadInterpFilter(ref ReadBitBuffer rb) + { + return rb.ReadBit() != 0 + ? (byte)Constants.Switchable + : _literalToFilter[rb.ReadLiteral(2)]; + } + + private static void SetupRenderSize(ref Vp9Common cm, ref ReadBitBuffer rb) + { + cm.RenderWidth = cm.Width; + cm.RenderHeight = cm.Height; + if (rb.ReadBit() != 0) + { + rb.ReadFrameSize(out cm.RenderWidth, out cm.RenderHeight); + } + } + + private static void SetupFrameSize(MemoryAllocator allocator, ref Vp9Common cm, ref ReadBitBuffer rb) + { + int width = 0, height = 0; + ref BufferPool pool = ref cm.BufferPool.Value; + rb.ReadFrameSize(out width, out height); + cm.ResizeContextBuffers(allocator, width, height); + SetupRenderSize(ref cm, ref rb); + + if (cm.GetFrameNewBuffer().ReallocFrameBuffer( + allocator, + cm.Width, + cm.Height, + cm.SubsamplingX, + cm.SubsamplingY, + cm.UseHighBitDepth, + Surface.DecBorderInPixels, + cm.ByteAlignment, + new Ptr(ref pool.FrameBufs[cm.NewFbIdx].RawFrameBuffer), + FrameBuffers.GetFrameBuffer, + pool.CbPriv) != 0) + { + cm.Error.InternalError(CodecErr.MemError, "Failed to allocate frame buffer"); + } + + pool.FrameBufs[cm.NewFbIdx].Released = 0; + pool.FrameBufs[cm.NewFbIdx].Buf.SubsamplingX = cm.SubsamplingX; + pool.FrameBufs[cm.NewFbIdx].Buf.SubsamplingY = cm.SubsamplingY; + pool.FrameBufs[cm.NewFbIdx].Buf.BitDepth = (uint)cm.BitDepth; + pool.FrameBufs[cm.NewFbIdx].Buf.ColorSpace = cm.ColorSpace; + pool.FrameBufs[cm.NewFbIdx].Buf.ColorRange = cm.ColorRange; + pool.FrameBufs[cm.NewFbIdx].Buf.RenderWidth = cm.RenderWidth; + pool.FrameBufs[cm.NewFbIdx].Buf.RenderHeight = cm.RenderHeight; + } + + private static bool ValidRefFrameImgFmt( + BitDepth refBitDepth, + int refXss, int refYss, + BitDepth thisBitDepth, + int thisXss, + int thisYss) + { + return refBitDepth == thisBitDepth && refXss == thisXss && refYss == thisYss; + } + + private static void SetupFrameSizeWithRefs(MemoryAllocator allocator, ref Vp9Common cm, + ref ReadBitBuffer rb) + { + int width = 0, height = 0; + bool found = false; + + bool hasValidRefFrame = false; + ref BufferPool pool = ref cm.BufferPool.Value; + for (int i = 0; i < Constants.RefsPerFrame; ++i) + { + if (rb.ReadBit() != 0) + { + if (cm.FrameRefs[i].Idx != RefBuffer.InvalidIdx) + { + ref Surface buf = ref cm.FrameRefs[i].Buf; + width = buf.YCropWidth; + height = buf.YCropHeight; + found = true; + break; + } + + cm.Error.InternalError(CodecErr.CorruptFrame, "Failed to decode frame size"); + } + } + + if (!found) + { + rb.ReadFrameSize(out width, out height); + } + + if (width <= 0 || height <= 0) + { + cm.Error.InternalError(CodecErr.CorruptFrame, "Invalid frame size"); + } + + // Check to make sure at least one of frames that this frame references + // has valid dimensions. + for (int i = 0; i < Constants.RefsPerFrame; ++i) + { + ref RefBuffer refFrame = ref cm.FrameRefs[i]; + hasValidRefFrame |= + refFrame.Idx != RefBuffer.InvalidIdx && + ScaleFactors.ValidRefFrameSize(refFrame.Buf.YCropWidth, refFrame.Buf.YCropHeight, width, + height); + } + + if (!hasValidRefFrame) + { + cm.Error.InternalError(CodecErr.CorruptFrame, "Referenced frame has invalid size"); + } + + for (int i = 0; i < Constants.RefsPerFrame; ++i) + { + ref RefBuffer refFrame = ref cm.FrameRefs[i]; + if (refFrame.Idx == RefBuffer.InvalidIdx || + !ValidRefFrameImgFmt( + (BitDepth)refFrame.Buf.BitDepth, + refFrame.Buf.SubsamplingX, + refFrame.Buf.SubsamplingY, + cm.BitDepth, + cm.SubsamplingX, + cm.SubsamplingY)) + { + cm.Error.InternalError(CodecErr.CorruptFrame, + "Referenced frame has incompatible color format"); + } + } + + cm.ResizeContextBuffers(allocator, width, height); + SetupRenderSize(ref cm, ref rb); + + if (cm.GetFrameNewBuffer().ReallocFrameBuffer( + allocator, + cm.Width, + cm.Height, + cm.SubsamplingX, + cm.SubsamplingY, + cm.UseHighBitDepth, + Surface.DecBorderInPixels, + cm.ByteAlignment, + new Ptr(ref pool.FrameBufs[cm.NewFbIdx].RawFrameBuffer), + FrameBuffers.GetFrameBuffer, + pool.CbPriv) != 0) + { + cm.Error.InternalError(CodecErr.MemError, "Failed to allocate frame buffer"); + } + + pool.FrameBufs[cm.NewFbIdx].Released = 0; + pool.FrameBufs[cm.NewFbIdx].Buf.SubsamplingX = cm.SubsamplingX; + pool.FrameBufs[cm.NewFbIdx].Buf.SubsamplingY = cm.SubsamplingY; + pool.FrameBufs[cm.NewFbIdx].Buf.BitDepth = (uint)cm.BitDepth; + pool.FrameBufs[cm.NewFbIdx].Buf.ColorSpace = cm.ColorSpace; + pool.FrameBufs[cm.NewFbIdx].Buf.ColorRange = cm.ColorRange; + pool.FrameBufs[cm.NewFbIdx].Buf.RenderWidth = cm.RenderWidth; + pool.FrameBufs[cm.NewFbIdx].Buf.RenderHeight = cm.RenderHeight; + } + // Reads the next tile returning its size and adjusting '*data' accordingly // based on 'isLast'. private static void GetTileBuffer( @@ -1102,7 +1415,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { if (!ReadIsValid(data, 4)) { - errorInfo.InternalError(CodecErr.CodecCorruptFrame, "Truncated packet or corrupt tile length"); + errorInfo.InternalError(CodecErr.CorruptFrame, "Truncated packet or corrupt tile length"); } size = BinaryPrimitives.ReadInt32BigEndian(data.AsSpan()); @@ -1110,7 +1423,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 if (size > data.Length) { - errorInfo.InternalError(CodecErr.CodecCorruptFrame, "Truncated packet or corrupt tile size"); + errorInfo.InternalError(CodecErr.CorruptFrame, "Truncated packet or corrupt tile size"); } } else @@ -1124,11 +1437,10 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 data = data.Slice(size); } - private static void GetTileBuffers(ref Vp9Common cm, ArrayPtr data, int tileCols, ref Array64 tileBuffers) + private static void GetTileBuffers(ref Vp9Common cm, ArrayPtr data, int tileCols, + ref Array64 tileBuffers) { - int c; - - for (c = 0; c < tileCols; ++c) + for (int c = 0; c < tileCols; ++c) { bool isLast = c == tileCols - 1; ref TileBuffer buf = ref tileBuffers[c]; @@ -1144,13 +1456,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 int tileRows, ref Array4> tileBuffers) { - int r, c; - - for (r = 0; r < tileRows; ++r) + for (int r = 0; r < tileRows; ++r) { - for (c = 0; c < tileCols; ++c) + for (int c = 0; c < tileCols; ++c) { - bool isLast = (r == tileRows - 1) && (c == tileCols - 1); + bool isLast = r == tileRows - 1 && c == tileCols - 1; ref TileBuffer buf = ref tileBuffers[r][c]; GetTileBuffer(isLast, ref cm.Error, ref data, ref buf); } @@ -1167,7 +1477,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 int miRow, miCol; Debug.Assert(tileRows <= 4); - Debug.Assert(tileCols <= (1 << 6)); + Debug.Assert(tileCols <= 1 << 6); // Note: this memset assumes above_context[0], [1] and [2] // are allocated as part of the same buffer. @@ -1183,7 +1493,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 for (tileCol = 0; tileCol < tileCols; ++tileCol) { ref TileBuffer buf = ref tileBuffers[tileRow][tileCol]; - ref TileWorkerData tileData = ref cm.TileWorkerData[tileCols * tileRow + tileCol]; + ref TileWorkerData tileData = ref cm.TileWorkerData[(tileCols * tileRow) + tileCol]; tileData.Xd = cm.Mb; tileData.Xd.Corrupted = false; tileData.Xd.Counts = cm.Counts; @@ -1203,28 +1513,30 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 for (tileCol = 0; tileCol < tileCols; ++tileCol) { int col = tileCol; - ref TileWorkerData tileData = ref cm.TileWorkerData[tileCols * tileRow + col]; + ref TileWorkerData tileData = ref cm.TileWorkerData[(tileCols * tileRow) + col]; tile.SetCol(ref cm, col); tileData.Xd.LeftContext = new Array3>(); tileData.Xd.LeftSegContext = new Array8(); for (miCol = tile.MiColStart; miCol < tile.MiColEnd; miCol += Constants.MiBlockSize) { - DecodePartition(ref tileData, ref cm, miRow, miCol, BlockSize.Block64x64, 4); + DecodePartition(ref tileData, ref cm, miRow, miCol, BlockSize.Block64X64, 4); } + cm.Mb.Corrupted |= tileData.Xd.Corrupted; if (cm.Mb.Corrupted) { - cm.Error.InternalError(CodecErr.CodecCorruptFrame, "Failed to decode tile data"); + cm.Error.InternalError(CodecErr.CorruptFrame, "Failed to decode tile data"); } } } } // Get last tile data. - return cm.TileWorkerData[tileCols * tileRows - 1].BitReader.FindEnd(); + return cm.TileWorkerData[(tileCols * tileRows) - 1].BitReader.FindEnd(); } - private static bool DecodeTileCol(ref TileWorkerData tileData, ref Vp9Common cm, ref Array64 tileBuffers) + private static bool DecodeTileCol(ref TileWorkerData tileData, ref Vp9Common cm, + ref Array64 tileBuffers) { ref TileInfo tile = ref tileData.Xd.Tile; int finalCol = (1 << cm.Log2TileCols) - 1; @@ -1251,7 +1563,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 tileData.Xd.LeftSegContext = new Array8(); for (int miCol = tile.MiColStart; miCol < tile.MiColEnd; miCol += Constants.MiBlockSize) { - DecodePartition(ref tileData, ref cm, miRow, miCol, BlockSize.Block64x64, 4); + DecodePartition(ref tileData, ref cm, miRow, miCol, BlockSize.Block64X64, 4); } } @@ -1262,7 +1574,6 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 } while (!tileData.Xd.Corrupted && ++n <= tileData.BufEnd); tileData.DataEnd = bitReaderEnd; - return !tileData.Xd.Corrupted; } @@ -1276,9 +1587,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 int numWorkers = Math.Min(maxThreads, tileCols); int n; - Debug.Assert(tileCols <= (1 << 6)); + Debug.Assert(tileCols <= 1 << 6); Debug.Assert(tileRows == 1); + LoopFilter.ResetLfm(ref cm); + cm.AboveContext.AsSpan().Clear(); cm.AboveSegContext.AsSpan().Clear(); @@ -1295,13 +1608,13 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 GetTileBuffers(ref cm, data, tileCols, ref tileBuffers); - tileBuffers.AsSpan()[..tileCols].Sort(CompareTileBuffers); + tileBuffers.AsSpan().Slice(0, tileCols).Sort(CompareTileBuffers); if (numWorkers == tileCols) { TileBuffer largest = tileBuffers[0]; Span buffers = tileBuffers.AsSpan(); - buffers[1..].CopyTo(buffers[..(tileBuffers.Length - 1)]); + buffers.Slice(1).CopyTo(buffers.Slice(0, tileBuffers.Length - 1)); tileBuffers[tileCols - 1] = largest; } else @@ -1327,7 +1640,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 for (n = 0; n < numWorkers; ++n) { - int count = baseVal + (remain + n) / numWorkers; + int count = baseVal + ((remain + n) / numWorkers); ref TileWorkerData tileData = ref cm.TileWorkerData[n + totalTiles]; tileData.BufStart = bufStart; @@ -1364,7 +1677,6 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 } Debug.Assert(!bitReaderEnd.IsNull || cm.Mb.Corrupted); - return bitReaderEnd; } @@ -1383,5 +1695,477 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 a[i] += c[i]; } } + + private static void ErrorHandler(Ptr data) + { + ref Vp9Common cm = ref data.Value; + cm.Error.InternalError(CodecErr.CorruptFrame, "Truncated packet"); + } + + private static void FlushAllFbOnKey(ref Vp9Common cm) + { + if (cm.FrameType == FrameType.KeyFrame && cm.CurrentVideoFrame > 0) + { + ref Array12 frameBufs = ref cm.BufferPool.Value.FrameBufs; + ref BufferPool pool = ref cm.BufferPool.Value; + + for (int i = 0; i < Constants.FrameBuffers; ++i) + { + if (i == cm.NewFbIdx) + { + continue; + } + + frameBufs[i].RefCount = 0; + if (frameBufs[i].Released == 0) + { + FrameBuffers.ReleaseFrameBuffer(pool.CbPriv, ref frameBufs[i].RawFrameBuffer); + frameBufs[i].Released = 1; + } + } + } + } + + private const int SyncCode0 = 0x49; + private const int SyncCode1 = 0x83; + private const int SyncCode2 = 0x42; + + private const int FrameMarker = 0x2; + + private static bool ReadSyncCode(ref ReadBitBuffer rb) + { + return rb.ReadLiteral(8) == SyncCode0 && + rb.ReadLiteral(8) == SyncCode1 && + rb.ReadLiteral(8) == SyncCode2; + } + + private static void RefCntFb(ref Array12 bufs, ref int idx, int newIdx) + { + int refIndex = idx; + + if (refIndex >= 0 && bufs[refIndex].RefCount > 0) + { + bufs[refIndex].RefCount--; + } + + idx = newIdx; + + bufs[newIdx].RefCount++; + } + + private static ulong ReadUncompressedHeader(MemoryAllocator allocator, ref Vp9Decoder pbi, + ref ReadBitBuffer rb) + { + ref Vp9Common cm = ref pbi.Common; + ref BufferPool pool = ref cm.BufferPool.Value; + ref Array12 frameBufs = ref pool.FrameBufs; + int mask, refIndex = 0; + ulong sz; + + cm.LastFrameType = cm.FrameType; + cm.LastIntraOnly = cm.IntraOnly; + + if (rb.ReadLiteral(2) != FrameMarker) + { + cm.Error.InternalError(CodecErr.UnsupBitstream, "Invalid frame marker"); + } + + cm.Profile = rb.ReadProfile(); + if (cm.Profile >= BitstreamProfile.MaxProfiles) + { + cm.Error.InternalError(CodecErr.UnsupBitstream, "Unsupported bitstream profile"); + } + + cm.ShowExistingFrame = rb.ReadBit(); + if (cm.ShowExistingFrame != 0) + { + // Show an existing frame directly. + int frameToShow = cm.RefFrameMap[rb.ReadLiteral(3)]; + if (frameToShow < 0 || frameBufs[frameToShow].RefCount < 1) + { + cm.Error.InternalError(CodecErr.UnsupBitstream, + $"Buffer {frameToShow} does not contain a decoded frame"); + } + + RefCntFb(ref frameBufs, ref cm.NewFbIdx, frameToShow); + pbi.RefreshFrameFlags = 0; + cm.Lf.FilterLevel = 0; + cm.ShowFrame = 1; + + return 0; + } + + cm.FrameType = (FrameType)rb.ReadBit(); + cm.ShowFrame = rb.ReadBit(); + cm.ErrorResilientMode = rb.ReadBit(); + + if (cm.FrameType == FrameType.KeyFrame) + { + if (!ReadSyncCode(ref rb)) + { + cm.Error.InternalError(CodecErr.UnsupBitstream, "Invalid frame sync code"); + } + + cm.ReadBitdepthColorspaceSampling(ref rb); + pbi.RefreshFrameFlags = (1 << Constants.RefFrames) - 1; + + for (int i = 0; i < Constants.RefsPerFrame; ++i) + { + cm.FrameRefs[i].Idx = RefBuffer.InvalidIdx; + cm.FrameRefs[i].Buf = default; + } + + SetupFrameSize(allocator, ref cm, ref rb); + if (pbi.NeedResync != 0) + { + cm.RefFrameMap.AsSpan().Fill(-1); + FlushAllFbOnKey(ref cm); + pbi.NeedResync = 0; + } + } + else + { + cm.IntraOnly = (cm.ShowFrame != 0 ? 0 : rb.ReadBit()) != 0; + + cm.ResetFrameContext = cm.ErrorResilientMode != 0 ? 0 : rb.ReadLiteral(2); + + if (cm.IntraOnly) + { + if (!ReadSyncCode(ref rb)) + { + cm.Error.InternalError(CodecErr.UnsupBitstream, "Invalid frame sync code"); + } + + if (cm.Profile > BitstreamProfile.Profile0) + { + cm.ReadBitdepthColorspaceSampling(ref rb); + } + else + { + // NOTE: The intra-only frame header does not include the specification + // of either the color format or color sub-sampling in profile 0. VP9 + // specifies that the default color format should be YUV 4:2:0 in this + // case (normative). + cm.ColorSpace = VpxColorSpace.Bt601; + cm.ColorRange = VpxColorRange.Studio; + cm.SubsamplingY = cm.SubsamplingX = 1; + cm.BitDepth = BitDepth.Bits8; + cm.UseHighBitDepth = false; + } + + pbi.RefreshFrameFlags = rb.ReadLiteral(Constants.RefFrames); + SetupFrameSize(allocator, ref cm, ref rb); + if (pbi.NeedResync != 0) + { + cm.RefFrameMap.AsSpan().Fill(-1); + pbi.NeedResync = 0; + } + } + else if (pbi.NeedResync != 1) + { + /* Skip if need resync */ + pbi.RefreshFrameFlags = rb.ReadLiteral(Constants.RefFrames); + for (int i = 0; i < Constants.RefsPerFrame; ++i) + { + int refr = rb.ReadLiteral(Constants.RefFramesLog2); + int idx = cm.RefFrameMap[refr]; + ref RefBuffer refFrame = ref cm.FrameRefs[i]; + refFrame.Idx = idx; + refFrame.Buf = frameBufs[idx].Buf; + cm.RefFrameSignBias[Constants.LastFrame + i] = (sbyte)rb.ReadBit(); + } + + SetupFrameSizeWithRefs(allocator, ref cm, ref rb); + + cm.AllowHighPrecisionMv = rb.ReadBit() != 0; + cm.InterpFilter = ReadInterpFilter(ref rb); + + for (int i = 0; i < Constants.RefsPerFrame; ++i) + { + ref RefBuffer refBuf = ref cm.FrameRefs[i]; + refBuf.Sf.SetupScaleFactorsForFrame( + refBuf.Buf.YCropWidth, + refBuf.Buf.YCropHeight, + cm.Width, + cm.Height); + } + } + } + + cm.GetFrameNewBuffer().BitDepth = (uint)cm.BitDepth; + cm.GetFrameNewBuffer().ColorSpace = cm.ColorSpace; + cm.GetFrameNewBuffer().ColorRange = cm.ColorRange; + cm.GetFrameNewBuffer().RenderWidth = cm.RenderWidth; + cm.GetFrameNewBuffer().RenderHeight = cm.RenderHeight; + + if (pbi.NeedResync != 0) + { + cm.Error.InternalError(CodecErr.CorruptFrame, + "Keyframe / intra-only frame required to reset decoder state"); + } + + if (cm.ErrorResilientMode == 0) + { + cm.RefreshFrameContext = rb.ReadBit(); + cm.FrameParallelDecodingMode = rb.ReadBit(); + if (cm.FrameParallelDecodingMode == 0) + { + cm.Counts.Value = new Vp9BackwardUpdates(); + } + } + else + { + cm.RefreshFrameContext = 0; + cm.FrameParallelDecodingMode = 1; + } + + // This flag will be overridden by the call to SetupPastIndependence + // below, forcing the use of context 0 for those frame types. + cm.FrameContextIdx = (uint)rb.ReadLiteral(Constants.FrameContextsLog2); + + // Generate next_ref_frame_map. + for (mask = pbi.RefreshFrameFlags; mask != 0; mask >>= 1) + { + if ((mask & 1) != 0) + { + cm.NextRefFrameMap[refIndex] = cm.NewFbIdx; + ++frameBufs[cm.NewFbIdx].RefCount; + } + else + { + cm.NextRefFrameMap[refIndex] = cm.RefFrameMap[refIndex]; + } + + // Current thread holds the reference frame. + if (cm.RefFrameMap[refIndex] >= 0) + { + ++frameBufs[cm.RefFrameMap[refIndex]].RefCount; + } + + ++refIndex; + } + + for (; refIndex < Constants.RefFrames; ++refIndex) + { + cm.NextRefFrameMap[refIndex] = cm.RefFrameMap[refIndex]; + // Current thread holds the reference frame. + if (cm.RefFrameMap[refIndex] >= 0) + { + ++frameBufs[cm.RefFrameMap[refIndex]].RefCount; + } + } + + pbi.HoldRefBuf = 1; + + if (cm.FrameIsIntraOnly() || cm.ErrorResilientMode != 0) + { + EntropyMode.SetupPastIndependence(ref cm); + } + + SetupLoopfilter(ref cm.Lf, ref rb); + SetupQuantization(ref cm, ref cm.Mb, ref rb); + cm.Seg.SetupSegmentation(ref cm.Fc.Value, ref rb); + cm.SetupSegmentationDequant(); + + cm.SetupTileInfo(ref rb); + sz = (ulong)rb.ReadLiteral(16); + + if (sz == 0) + { + cm.Error.InternalError(CodecErr.CorruptFrame, "Invalid header size"); + } + + return sz; + } + + private static bool ReadCompressedHeader(ref Vp9Decoder pbi, ArrayPtr data, ulong partitionSize) + { + ref Vp9Common cm = ref pbi.Common; + ref MacroBlockD xd = ref cm.Mb; + ref Vp9EntropyProbs fc = ref cm.Fc.Value; + Reader r = new(); + + if (r.Init(data, (int)partitionSize)) + { + cm.Error.InternalError(CodecErr.MemError, "Failed to allocate bool decoder 0"); + } + + cm.TxMode = xd.Lossless ? TxMode.Only4X4 : r.ReadTxMode(); + if (cm.TxMode == TxMode.TxModeSelect) + { + ReadTxModeProbs(ref fc, ref r); + } + + ReadCoefProbs(ref fc, cm.TxMode, ref r); + + for (int k = 0; k < Constants.SkipContexts; ++k) + { + r.DiffUpdateProb(ref fc.SkipProb[k]); + } + + if (!cm.FrameIsIntraOnly()) + { + ReadInterModeProbs(ref fc, ref r); + + if (cm.InterpFilter == Constants.Switchable) + { + ReadSwitchableInterpProbs(ref fc, ref r); + } + + for (int i = 0; i < Constants.IntraInterContexts; i++) + { + r.DiffUpdateProb( ref fc.IntraInterProb[i]); + } + + cm.ReferenceMode = cm.ReadFrameReferenceMode(ref r); + if (cm.ReferenceMode != ReferenceMode.Single) + { + cm.SetupCompoundReferenceMode(); + } + + cm.ReadFrameReferenceModeProbs(ref r); + + for (int j = 0; j < EntropyMode.BlockSizeGroups; j++) + { + for (int i = 0; i < Constants.IntraModes - 1; ++i) + { + r.DiffUpdateProb( ref fc.YModeProb[j][i]); + } + } + + for (int j = 0; j < Constants.PartitionContexts; ++j) + { + for (int i = 0; i < Constants.PartitionTypes - 1; ++i) + { + r.DiffUpdateProb( ref fc.PartitionProb[j][i]); + } + } + + ReadMvProbs(ref fc, cm.AllowHighPrecisionMv, ref r); + } + + return r.HasError(); + } + + private static ref ReadBitBuffer InitReadBitBuffer(ref ReadBitBuffer rb, ReadOnlySpan data) + { + rb.BitOffset = 0; + rb.BitBuffer = data; + return ref rb; + } + + public static unsafe void Decode(MemoryAllocator allocator, + ref Vp9Decoder pbi, + ArrayPtr data, + out ArrayPtr pDataEnd, + bool multithreaded = true) + { + ref Vp9Common cm = ref pbi.Common; + ref MacroBlockD xd = ref cm.Mb; + ReadBitBuffer rb = new(); + int contextUpdated = 0; + Span clearData = stackalloc byte[80]; + ulong firstPartitionSize = + ReadUncompressedHeader(allocator, ref pbi, ref InitReadBitBuffer(ref rb, data.AsSpan())); + int tileRows = 1 << cm.Log2TileRows; + int tileCols = 1 << cm.Log2TileCols; + ref Surface newFb = ref cm.GetFrameNewBuffer(); + xd.CurBuf = newFb; + + if (firstPartitionSize == 0) + { + // showing a frame directly + pDataEnd = data.Slice(cm.Profile <= BitstreamProfile.Profile2 ? 1 : 2); + return; + } + + data = data.Slice((int)rb.BytesRead()); + if (!ReadIsValid(data, (int)firstPartitionSize)) + { + cm.Error.InternalError(CodecErr.CorruptFrame, "Truncated packet or corrupt header length"); + } + + cm.UsePrevFrameMvs = + cm.ErrorResilientMode == 0 && + cm.Width == cm.LastWidth && + cm.Height == cm.LastHeight && + !cm.LastIntraOnly && + cm.LastShowFrame != 0 && + cm.LastFrameType != FrameType.KeyFrame; + + xd.SetupBlockPlanes(cm.SubsamplingX, cm.SubsamplingY); + + cm.Fc = new Ptr(ref cm.FrameContexts[(int)cm.FrameContextIdx]); + + xd.Corrupted = false; + newFb.Corrupted = ReadCompressedHeader(ref pbi, data, firstPartitionSize) ? 1 : 0; + if (newFb.Corrupted != 0) + { + cm.Error.InternalError(CodecErr.CorruptFrame, "Decode failed. Frame data header is corrupted."); + } + + if (cm.Lf.FilterLevel != 0 && cm.SkipLoopFilter == 0) + { + LoopFilter.LoopFilterFrameInit(ref cm, cm.Lf.FilterLevel); + } + + int threadCount = multithreaded ? Math.Max(1, Environment.ProcessorCount / 2) : 0; + + if (cm.TileWorkerData.IsNull || tileCols * tileRows != cm.TotalTiles) + { + int numTileWorkers = (tileCols * tileRows) + threadCount; + if (!cm.TileWorkerData.IsNull) + { + allocator.Free(cm.TileWorkerData); + } + + cm.CheckMemError( ref cm.TileWorkerData, allocator.Allocate(numTileWorkers)); + cm.TotalTiles = tileRows * tileCols; + } + + if (multithreaded) + { + pDataEnd = DecodeTilesMt(ref pbi.Common, data.Slice((int)firstPartitionSize), threadCount); + + LoopFilter.LoopFilterFrameMt( + ref cm.Mb.CurBuf, + ref cm, + ref cm.Mb, + cm.Lf.FilterLevel, + false, + false, + threadCount); + } + else + { + pDataEnd = DecodeTiles(ref pbi.Common, data.Slice((int)firstPartitionSize)); + + LoopFilter.LoopFilterFrame(ref cm.Mb.CurBuf, ref cm, ref cm.Mb, cm.Lf.FilterLevel, false, false); + } + + if (!xd.Corrupted) + { + if (cm.ErrorResilientMode == 0 && cm.FrameParallelDecodingMode == 0) + { + cm.AdaptCoefProbs(); + + if (!cm.FrameIsIntraOnly()) + { + cm.AdaptModeProbs(); + cm.AdaptMvProbs(cm.AllowHighPrecisionMv); + } + } + } + else + { + cm.Error.InternalError(CodecErr.CorruptFrame, "Decode failed. Frame data is corrupted."); + } + + // Non frame parallel update frame context here. + if (cm.RefreshFrameContext != 0 && contextUpdated == 0) + { + cm.FrameContexts[(int)cm.FrameContextIdx] = cm.Fc.Value; + } + } } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/DecodeMv.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/DecodeMv.cs index 091490bfa..8b7ad4de1 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/DecodeMv.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/DecodeMv.cs @@ -1,4 +1,4 @@ -using Ryujinx.Common.Memory; +using Ryujinx.Common.Memory; using Ryujinx.Graphics.Nvdec.Vp9.Dsp; using Ryujinx.Graphics.Nvdec.Vp9.Types; using Ryujinx.Graphics.Video; @@ -10,11 +10,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { internal static class DecodeMv { - private const int MvrefNeighbours = 8; + private const int RefNeighbours = 8; private static PredictionMode ReadIntraMode(ref Reader r, ReadOnlySpan p) { - return (PredictionMode)r.ReadTree(Luts.Vp9IntraModeTree, p); + return (PredictionMode)r.ReadTree(Luts.IntraModeTree, p); } private static PredictionMode ReadIntraModeY(ref Vp9Common cm, ref MacroBlockD xd, ref Reader r, int sizeGroup) @@ -41,7 +41,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 private static PredictionMode ReadInterMode(ref Vp9Common cm, ref MacroBlockD xd, ref Reader r, int ctx) { - int mode = r.ReadTree(Luts.Vp9InterModeTree, cm.Fc.Value.InterModeProb[ctx].AsSpan()); + int mode = r.ReadTree(Luts.InterModeTree, cm.Fc.Value.InterModeProb[ctx].AsSpan()); if (!xd.Counts.IsNull) { ++xd.Counts.Value.InterMode[ctx][mode]; @@ -52,22 +52,18 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 private static int ReadSegmentId(ref Reader r, ref Array7 segTreeProbs) { - return r.ReadTree(Luts.Vp9SegmentTree, segTreeProbs.AsSpan()); + return r.ReadTree(Luts.SegmentTree, segTreeProbs.AsSpan()); } private static ReadOnlySpan GetTxProbs(ref Vp9EntropyProbs fc, TxSize maxTxSize, int ctx) { switch (maxTxSize) { - case TxSize.Tx8x8: - return fc.Tx8x8Prob[ctx].AsSpan(); - case TxSize.Tx16x16: - return fc.Tx16x16Prob[ctx].AsSpan(); - case TxSize.Tx32x32: - return fc.Tx32x32Prob[ctx].AsSpan(); + case TxSize.Tx8X8: return fc.Tx8x8Prob[ctx].AsSpan(); + case TxSize.Tx16X16: return fc.Tx16x16Prob[ctx].AsSpan(); + case TxSize.Tx32X32: return fc.Tx32x32Prob[ctx].AsSpan(); default: Debug.Assert(false, "Invalid maxTxSize."); - return ReadOnlySpan.Empty; } } @@ -76,15 +72,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { switch (maxTxSize) { - case TxSize.Tx8x8: - return counts.Tx8x8[ctx].AsSpan(); - case TxSize.Tx16x16: - return counts.Tx16x16[ctx].AsSpan(); - case TxSize.Tx32x32: - return counts.Tx32x32[ctx].AsSpan(); + case TxSize.Tx8X8: return counts.Tx8x8[ctx].AsSpan(); + case TxSize.Tx16X16: return counts.Tx16x16[ctx].AsSpan(); + case TxSize.Tx32X32: return counts.Tx32x32[ctx].AsSpan(); default: Debug.Assert(false, "Invalid maxTxSize."); - return Span.Empty; } } @@ -94,10 +86,10 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 int ctx = xd.GetTxSizeContext(); ReadOnlySpan txProbs = GetTxProbs(ref cm.Fc.Value, maxTxSize, ctx); TxSize txSize = (TxSize)r.Read(txProbs[0]); - if (txSize != TxSize.Tx4x4 && maxTxSize >= TxSize.Tx16x16) + if (txSize != TxSize.Tx4X4 && maxTxSize >= TxSize.Tx16X16) { txSize += r.Read(txProbs[1]); - if (txSize != TxSize.Tx8x8 && maxTxSize >= TxSize.Tx32x32) + if (txSize != TxSize.Tx8X8 && maxTxSize >= TxSize.Tx32X32) { txSize += r.Read(txProbs[2]); } @@ -116,7 +108,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 TxMode txMode = cm.TxMode; BlockSize bsize = xd.Mi[0].Value.SbType; TxSize maxTxSize = Luts.MaxTxSizeLookup[(int)bsize]; - if (allowSelect && txMode == TxMode.TxModeSelect && bsize >= BlockSize.Block8x8) + if (allowSelect && txMode == TxMode.TxModeSelect && bsize >= BlockSize.Block8X8) { return ReadSelectedTxSize(ref cm, ref xd, maxTxSize, ref r); } @@ -124,34 +116,32 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 return (TxSize)Math.Min((int)maxTxSize, (int)Luts.TxModeToBiggestTxSize[(int)txMode]); } - private static int DecGetSegmentId(ref Vp9Common cm, ArrayPtr segmentIds, int miOffset, int xMis, int yMis) + private static int DecGetSegmentId(ref Vp9Common cm, ArrayPtr segmentIds, int miOffset, int xMis, + int yMis) { - int x, y, segmentId = int.MaxValue; + int segmentId = int.MaxValue; - for (y = 0; y < yMis; y++) + for (int y = 0; y < yMis; y++) { - for (x = 0; x < xMis; x++) + for (int x = 0; x < xMis; x++) { - segmentId = Math.Min(segmentId, segmentIds[miOffset + y * cm.MiCols + x]); + segmentId = Math.Min(segmentId, segmentIds[miOffset + (y * cm.MiCols) + x]); } } Debug.Assert(segmentId >= 0 && segmentId < Constants.MaxSegments); - return segmentId; } private static void SetSegmentId(ref Vp9Common cm, int miOffset, int xMis, int yMis, int segmentId) { - int x, y; - Debug.Assert(segmentId >= 0 && segmentId < Constants.MaxSegments); - for (y = 0; y < yMis; y++) + for (int y = 0; y < yMis; y++) { - for (x = 0; x < xMis; x++) + for (int x = 0; x < xMis; x++) { - cm.CurrentFrameSegMap[miOffset + y * cm.MiCols + x] = (byte)segmentId; + cm.CurrentFrameSegMap[miOffset + (y * cm.MiCols) + x] = (byte)segmentId; } } } @@ -164,13 +154,13 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 int xMis, int yMis) { - int x, y; - - for (y = 0; y < yMis; y++) + for (int y = 0; y < yMis; y++) { - for (x = 0; x < xMis; x++) + for (int x = 0; x < xMis; x++) { - currentSegmentIds[miOffset + y * cm.MiCols + x] = (byte)(!lastSegmentIds.IsNull ? lastSegmentIds[miOffset + y * cm.MiCols + x] : 0); + currentSegmentIds[miOffset + (y * cm.MiCols) + x] = (byte)(!lastSegmentIds.IsNull + ? lastSegmentIds[miOffset + (y * cm.MiCols) + x] + : 0); } } } @@ -188,13 +178,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 if (!seg.UpdateMap) { CopySegmentId(ref cm, cm.LastFrameSegMap, cm.CurrentFrameSegMap, miOffset, xMis, yMis); - return 0; } segmentId = ReadSegmentId(ref r, ref cm.Fc.Value.SegTreeProb); SetSegmentId(ref cm, miOffset, xMis, yMis, segmentId); - return segmentId; } @@ -210,7 +198,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 ref Segmentation seg = ref cm.Seg; ref ModeInfo mi = ref xd.Mi[0].Value; int predictedSegmentId, segmentId; - int miOffset = miRow * cm.MiCols + miCol; + int miOffset = (miRow * cm.MiCols) + miCol; if (!seg.Enabled) { @@ -224,7 +212,6 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 if (!seg.UpdateMap) { CopySegmentId(ref cm, cm.LastFrameSegMap, cm.CurrentFrameSegMap, miOffset, xMis, yMis); - return predictedSegmentId; } @@ -232,20 +219,22 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { byte predProb = Segmentation.GetPredProbSegId(ref cm.Fc.Value.SegPredProb, ref xd); mi.SegIdPredicted = (sbyte)r.Read(predProb); - segmentId = mi.SegIdPredicted != 0 ? predictedSegmentId : ReadSegmentId(ref r, ref cm.Fc.Value.SegTreeProb); + segmentId = mi.SegIdPredicted != 0 + ? predictedSegmentId + : ReadSegmentId(ref r, ref cm.Fc.Value.SegTreeProb); } else { segmentId = ReadSegmentId(ref r, ref cm.Fc.Value.SegTreeProb); } - SetSegmentId(ref cm, miOffset, xMis, yMis, segmentId); + SetSegmentId(ref cm, miOffset, xMis, yMis, segmentId); return segmentId; } private static int ReadSkip(ref Vp9Common cm, ref MacroBlockD xd, int segmentId, ref Reader r) { - if (cm.Seg.IsSegFeatureActive(segmentId, SegLvlFeatures.SegLvlSkip) != 0) + if (cm.Seg.IsSegFeatureActive(segmentId, SegLvlFeatures.Skip) != 0) { return 1; } @@ -260,12 +249,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 return skip; } - private static int ReadMvComponent(ref Reader r, ref Vp9EntropyProbs fc, int mvcomp, bool usehp) + private static int ReadComponent(ref Reader r, ref Vp9EntropyProbs fc, int mvcomp, bool usehp) { int mag, d, fr, hp; bool sign = r.Read(fc.Sign[mvcomp]) != 0; - MvClassType mvClass = (MvClassType)r.ReadTree(Luts.Vp9MvClassTree, fc.Classes[mvcomp].AsSpan()); - bool class0 = mvClass == MvClassType.MvClass0; + MvClassType mvClass = (MvClassType)r.ReadTree(Luts.MvClassTree, fc.Classes[mvcomp].AsSpan()); + bool class0 = mvClass == MvClassType.Class0; // Integer part if (class0) @@ -275,11 +264,10 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 } else { - int i; int n = (int)mvClass + Constants.Class0Bits - 1; // Number of bits d = 0; - for (i = 0; i < n; ++i) + for (int i = 0; i < n; ++i) { d |= r.Read(fc.Bits[mvcomp][i]) << i; } @@ -288,40 +276,39 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 } // Fractional part - fr = r.ReadTree(Luts.Vp9MvFPTree, class0 ? fc.Class0Fp[mvcomp][d].AsSpan() : fc.Fp[mvcomp].AsSpan()); + fr = r.ReadTree(Luts.MvFpTree, class0 ? fc.Class0Fp[mvcomp][d].AsSpan() : fc.Fp[mvcomp].AsSpan()); // High precision part (if hp is not used, the default value of the hp is 1) hp = usehp ? r.Read(class0 ? fc.Class0Hp[mvcomp] : fc.Hp[mvcomp]) : 1; // Result mag += ((d << 3) | (fr << 1) | hp) + 1; - return sign ? -mag : mag; } - private static void ReadMv( + private static void Read( ref Reader r, ref Mv mv, ref Mv refr, ref Vp9EntropyProbs fc, Ptr counts, - bool allowHP) + bool allowHp) { - MvJointType jointType = (MvJointType)r.ReadTree(Luts.Vp9MvJointTree, fc.Joints.AsSpan()); - bool useHP = allowHP && refr.UseMvHp(); + MvJointType jointType = (MvJointType)r.ReadTree(Luts.MvJointTree, fc.Joints.AsSpan()); + bool useHp = allowHp && refr.UseHp(); Mv diff = new(); - if (Mv.MvJointVertical(jointType)) + if (Mv.JointVertical(jointType)) { - diff.Row = (short)ReadMvComponent(ref r, ref fc, 0, useHP); + diff.Row = (short)ReadComponent(ref r, ref fc, 0, useHp); } - if (Mv.MvJointHorizontal(jointType)) + if (Mv.JointHorizontal(jointType)) { - diff.Col = (short)ReadMvComponent(ref r, ref fc, 1, useHP); + diff.Col = (short)ReadComponent(ref r, ref fc, 1, useHp); } - diff.IncMv(counts); + diff.Inc(counts); mv.Row = (short)(refr.Row + diff.Row); mv.Col = (short)(refr.Col + diff.Col); @@ -329,7 +316,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 private static ReferenceMode ReadBlockReferenceMode(ref Vp9Common cm, ref MacroBlockD xd, ref Reader r) { - if (cm.ReferenceMode == ReferenceMode.ReferenceModeSelect) + if (cm.ReferenceMode == ReferenceMode.Select) { int ctx = PredCommon.GetReferenceModeContext(ref cm, ref xd); ReferenceMode mode = (ReferenceMode)r.Read(cm.Fc.Value.CompInterProb[ctx]); @@ -354,15 +341,15 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { ref Vp9EntropyProbs fc = ref cm.Fc.Value; - if (cm.Seg.IsSegFeatureActive(segmentId, SegLvlFeatures.SegLvlRefFrame) != 0) + if (cm.Seg.IsSegFeatureActive(segmentId, SegLvlFeatures.RefFrame) != 0) { - refFrame[0] = (sbyte)cm.Seg.GetSegData(segmentId, SegLvlFeatures.SegLvlRefFrame); + refFrame[0] = (sbyte)cm.Seg.GetSegData(segmentId, SegLvlFeatures.RefFrame); refFrame[1] = Constants.None; } else { ReferenceMode mode = ReadBlockReferenceMode(ref cm, ref xd, ref r); - if (mode == ReferenceMode.CompoundReference) + if (mode == ReferenceMode.Compound) { int idx = cm.RefFrameSignBias[cm.CompFixedRef]; int ctx = PredCommon.GetPredContextCompRefP(ref cm, ref xd); @@ -375,7 +362,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 refFrame[idx] = cm.CompFixedRef; refFrame[idx == 0 ? 1 : 0] = cm.CompVarRef[bit]; } - else if (mode == ReferenceMode.SingleReference) + else if (mode == ReferenceMode.Single) { int ctx0 = PredCommon.GetPredContextSingleRefP1(ref xd); int bit0 = r.Read(fc.SingleRefProb[ctx0][0]); @@ -412,7 +399,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 private static byte ReadSwitchableInterpFilter(ref Vp9Common cm, ref MacroBlockD xd, ref Reader r) { int ctx = xd.GetPredContextSwitchableInterp(); - byte type = (byte)r.ReadTree(Luts.Vp9SwitchableInterpTree, cm.Fc.Value.SwitchableInterpProb[ctx].AsSpan()); + byte type = (byte)r.ReadTree(Luts.SwitchableInterpTree, cm.Fc.Value.SwitchableInterpProb[ctx].AsSpan()); if (!xd.Counts.IsNull) { ++xd.Counts.Value.SwitchableInterp[ctx][type]; @@ -424,23 +411,23 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 private static void ReadIntraBlockModeInfo(ref Vp9Common cm, ref MacroBlockD xd, ref ModeInfo mi, ref Reader r) { BlockSize bsize = mi.SbType; - int i; + switch (bsize) { - case BlockSize.Block4x4: - for (i = 0; i < 4; ++i) + case BlockSize.Block4X4: + for (int i = 0; i < 4; ++i) { mi.Bmi[i].Mode = ReadIntraModeY(ref cm, ref xd, ref r, 0); } mi.Mode = mi.Bmi[3].Mode; break; - case BlockSize.Block4x8: + case BlockSize.Block4X8: mi.Bmi[0].Mode = mi.Bmi[2].Mode = ReadIntraModeY(ref cm, ref xd, ref r, 0); mi.Bmi[1].Mode = mi.Bmi[3].Mode = mi.Mode = ReadIntraModeY(ref cm, ref xd, ref r, 0); break; - case BlockSize.Block8x4: + case BlockSize.Block8X4: mi.Bmi[0].Mode = mi.Bmi[1].Mode = ReadIntraModeY(ref cm, ref xd, ref r, 0); mi.Bmi[2].Mode = mi.Bmi[3].Mode = mi.Mode = ReadIntraModeY(ref cm, ref xd, ref r, 0); break; @@ -459,27 +446,19 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 mi.RefFrame[1] = Constants.None; } - private static bool IsMvValid(ref Mv mv) - { - return mv.Row > Constants.MvLow && - mv.Row < Constants.MvUpp && - mv.Col > Constants.MvLow && - mv.Col < Constants.MvUpp; - } - - private static void CopyMvPair(ref Array2 dst, ref Array2 src) + private static void CopyPair(ref Array2 dst, ref Array2 src) { dst[0] = src[0]; dst[1] = src[1]; } - private static void ZeroMvPair(ref Array2 dst) + private static void ZeroPair(ref Array2 dst) { dst[0] = new Mv(); dst[1] = new Mv(); } - private static bool AssignMv( + private static bool Assign( ref Vp9Common cm, ref MacroBlockD xd, PredictionMode mode, @@ -487,45 +466,45 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 ref Array2 refMv, ref Array2 nearNearestMv, int isCompound, - bool allowHP, + bool allowHp, ref Reader r) { - int i; bool ret = true; switch (mode) { case PredictionMode.NewMv: { - for (i = 0; i < 1 + isCompound; ++i) + for (int i = 0; i < 1 + isCompound; ++i) { - ReadMv(ref r, ref mv[i], ref refMv[i], ref cm.Fc.Value, xd.Counts, allowHP); - ret = ret && IsMvValid(ref mv[i]); + Read(ref r, ref mv[i], ref refMv[i], ref cm.Fc.Value, xd.Counts, allowHp); + ret = ret && mv[i].IsValid(); } + break; } case PredictionMode.NearMv: case PredictionMode.NearestMv: { - CopyMvPair(ref mv, ref nearNearestMv); + CopyPair(ref mv, ref nearNearestMv); break; } case PredictionMode.ZeroMv: { - ZeroMvPair(ref mv); + ZeroPair(ref mv); break; } - default: - return false; + default: return false; } + return ret; } private static bool ReadIsInterBlock(ref Vp9Common cm, ref MacroBlockD xd, int segmentId, ref Reader r) { - if (cm.Seg.IsSegFeatureActive(segmentId, SegLvlFeatures.SegLvlRefFrame) != 0) + if (cm.Seg.IsSegFeatureActive(segmentId, SegLvlFeatures.RefFrame) != 0) { - return cm.Seg.GetSegData(segmentId, SegLvlFeatures.SegLvlRefFrame) != Constants.IntraFrame; + return cm.Seg.GetSegData(segmentId, SegLvlFeatures.RefFrame) != Constants.IntraFrame; } int ctx = xd.GetIntraInterContext(); @@ -538,33 +517,30 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 return isInter; } - private static void DecFindBestRefMvs(bool allowHP, Span mvlist, ref Mv bestMv, int refmvCount) + private static void DecFindBestRefs(bool allowHp, Span mvlist, ref Mv bestMv, int refmvCount) { - int i; - // Make sure all the candidates are properly clamped etc - for (i = 0; i < refmvCount; ++i) + for (int i = 0; i < refmvCount; ++i) { - mvlist[i].LowerMvPrecision(allowHP); + mvlist[i].LowerPrecision(allowHp); bestMv = mvlist[i]; } } - private static bool AddMvRefListEb(Mv mv, ref int refMvCount, Span mvRefList, bool earlyBreak) + private static bool AddRefListEb(Mv mv, ref int refCount, Span mvRefList, bool earlyBreak) { - if (refMvCount != 0) + if (refCount != 0) { if (Unsafe.As(ref mv) != Unsafe.As(ref mvRefList[0])) { - mvRefList[refMvCount] = mv; - refMvCount++; - + mvRefList[refCount] = mv; + refCount++; return true; } } else { - mvRefList[refMvCount++] = mv; + mvRefList[refCount++] = mv; if (earlyBreak) { return true; @@ -574,19 +550,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 return false; } - // Performs mv sign inversion if indicated by the reference frame combination. - private static Mv ScaleMv(ref ModeInfo mi, int refr, sbyte thisRefFrame, ref Array4 refSignBias) - { - Mv mv = mi.Mv[refr]; - if (refSignBias[mi.RefFrame[refr]] != refSignBias[thisRefFrame]) - { - mv.Row *= -1; - mv.Col *= -1; - } - return mv; - } - - private static bool IsDiffRefFrameAddMvEb( + private static bool IsDiffRefFrameAddEb( ref ModeInfo mbmi, sbyte refFrame, ref Array4 refSignBias, @@ -598,26 +562,30 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { if (mbmi.RefFrame[0] != refFrame) { - if (AddMvRefListEb(ScaleMv(ref mbmi, 0, refFrame, ref refSignBias), ref refmvCount, mvRefList, earlyBreak)) - { - return true; - } - } - if (mbmi.HasSecondRef() && mbmi.RefFrame[1] != refFrame && Unsafe.As(ref mbmi.Mv[1]) != Unsafe.As(ref mbmi.Mv[0])) - { - if (AddMvRefListEb(ScaleMv(ref mbmi, 1, refFrame, ref refSignBias), ref refmvCount, mvRefList, earlyBreak)) + if (AddRefListEb(mbmi.ScaleMv(0, refFrame, ref refSignBias), ref refmvCount, mvRefList, + earlyBreak)) { return true; } } + if (mbmi.HasSecondRef() && mbmi.RefFrame[1] != refFrame && + Unsafe.As(ref mbmi.Mv[1]) != Unsafe.As(ref mbmi.Mv[0])) + { + if (AddRefListEb(mbmi.ScaleMv(1, refFrame, ref refSignBias), ref refmvCount, mvRefList, + earlyBreak)) + { + return true; + } + } } + return false; } // This function searches the neighborhood of a given MB/SB // to try and find candidate reference vectors. - private static int DecFindMvRefs( + private static int DecFindRefs( ref Vp9Common cm, ref MacroBlockD xd, PredictionMode mode, @@ -632,14 +600,16 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 ref Array4 refSignBias = ref cm.RefFrameSignBias; int i, refmvCount = 0; bool differentRefFound = false; - Ptr prevFrameMvs = cm.UsePrevFrameMvs ? new Ptr(ref cm.PrevFrameMvs[miRow * cm.MiCols + miCol]) : Ptr.Null; + Ptr prevFrameMvs = cm.UsePrevFrameMvs + ? new Ptr(ref cm.PrevFrameMvs[(miRow * cm.MiCols) + miCol]) + : Ptr.Null; ref TileInfo tile = ref xd.Tile; // If mode is nearestmv or newmv (uses nearestmv as a reference) then stop // searching after the first mv is found. bool earlyBreak = mode != PredictionMode.NearMv; // Blank the reference vector list - mvRefList[..Constants.MaxMvRefCandidates].Clear(); + mvRefList.Slice(0, Constants.MaxMvRefCandidates).Fill(new Mv()); i = 0; if (isSub8X8 != 0) @@ -651,19 +621,21 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 ref Position mvRef = ref mvRefSearch[i]; if (tile.IsInside(miCol, miRow, cm.MiRows, ref mvRef)) { - ref ModeInfo candidateMi = ref xd.Mi[mvRef.Col + mvRef.Row * xd.MiStride].Value; + ref ModeInfo candidateMi = ref xd.Mi[mvRef.Col + (mvRef.Row * xd.MiStride)].Value; differentRefFound = true; if (candidateMi.RefFrame[0] == refFrame) { - if (AddMvRefListEb(candidateMi.GetSubBlockMv(0, mvRef.Col, block), ref refmvCount, mvRefList, earlyBreak)) + if (AddRefListEb(candidateMi.GetSubBlockMv(0, mvRef.Col, block), ref refmvCount, + mvRefList, earlyBreak)) { goto Done; } } else if (candidateMi.RefFrame[1] == refFrame) { - if (AddMvRefListEb(candidateMi.GetSubBlockMv(1, mvRef.Col, block), ref refmvCount, mvRefList, earlyBreak)) + if (AddRefListEb(candidateMi.GetSubBlockMv(1, mvRef.Col, block), ref refmvCount, + mvRefList, earlyBreak)) { goto Done; } @@ -675,24 +647,24 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 // Check the rest of the neighbors in much the same way // as before except we don't need to keep track of sub blocks or // mode counts. - for (; i < MvrefNeighbours; ++i) + for (; i < RefNeighbours; ++i) { ref Position mvRef = ref mvRefSearch[i]; if (tile.IsInside(miCol, miRow, cm.MiRows, ref mvRef)) { - ref ModeInfo candidate = ref xd.Mi[mvRef.Col + mvRef.Row * xd.MiStride].Value; + ref ModeInfo candidate = ref xd.Mi[mvRef.Col + (mvRef.Row * xd.MiStride)].Value; differentRefFound = true; if (candidate.RefFrame[0] == refFrame) { - if (AddMvRefListEb(candidate.Mv[0], ref refmvCount, mvRefList, earlyBreak)) + if (AddRefListEb(candidate.Mv[0], ref refmvCount, mvRefList, earlyBreak)) { goto Done; } } else if (candidate.RefFrame[1] == refFrame) { - if (AddMvRefListEb(candidate.Mv[1], ref refmvCount, mvRefList, earlyBreak)) + if (AddRefListEb(candidate.Mv[1], ref refmvCount, mvRefList, earlyBreak)) { goto Done; } @@ -705,14 +677,14 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { if (prevFrameMvs.Value.RefFrame[0] == refFrame) { - if (AddMvRefListEb(prevFrameMvs.Value.Mv[0], ref refmvCount, mvRefList, earlyBreak)) + if (AddRefListEb(prevFrameMvs.Value.Mv[0], ref refmvCount, mvRefList, earlyBreak)) { goto Done; } } else if (prevFrameMvs.Value.RefFrame[1] == refFrame) { - if (AddMvRefListEb(prevFrameMvs.Value.Mv[1], ref refmvCount, mvRefList, earlyBreak)) + if (AddRefListEb(prevFrameMvs.Value.Mv[1], ref refmvCount, mvRefList, earlyBreak)) { goto Done; } @@ -724,15 +696,16 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 // different reference frames. if (differentRefFound) { - for (i = 0; i < MvrefNeighbours; ++i) + for (i = 0; i < RefNeighbours; ++i) { ref Position mvRef = ref mvRefSearch[i]; if (tile.IsInside(miCol, miRow, cm.MiRows, ref mvRef)) { - ref ModeInfo candidate = ref xd.Mi[mvRef.Col + mvRef.Row * xd.MiStride].Value; + ref ModeInfo candidate = ref xd.Mi[mvRef.Col + (mvRef.Row * xd.MiStride)].Value; // If the candidate is Intra we don't want to consider its mv. - if (IsDiffRefFrameAddMvEb(ref candidate, refFrame, ref refSignBias, ref refmvCount, mvRefList, earlyBreak)) + if (IsDiffRefFrameAddEb(ref candidate, refFrame, ref refSignBias, ref refmvCount, mvRefList, + earlyBreak)) { goto Done; } @@ -751,7 +724,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 mv.Row *= -1; mv.Col *= -1; } - if (AddMvRefListEb(mv, ref refmvCount, mvRefList, earlyBreak)) + + if (AddRefListEb(mv, ref refmvCount, mvRefList, earlyBreak)) { goto Done; } @@ -759,7 +733,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 if (prevFrameMvs.Value.RefFrame[1] > Constants.IntraFrame && prevFrameMvs.Value.RefFrame[1] != refFrame && - Unsafe.As(ref prevFrameMvs.Value.Mv[1]) != Unsafe.As(ref prevFrameMvs.Value.Mv[0])) + Unsafe.As(ref prevFrameMvs.Value.Mv[1]) != + Unsafe.As(ref prevFrameMvs.Value.Mv[0])) { Mv mv = prevFrameMvs.Value.Mv[1]; if (refSignBias[prevFrameMvs.Value.RefFrame[1]] != refSignBias[refFrame]) @@ -767,7 +742,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 mv.Row *= -1; mv.Col *= -1; } - if (AddMvRefListEb(mv, ref refmvCount, mvRefList, earlyBreak)) + + if (AddRefListEb(mv, ref refmvCount, mvRefList, earlyBreak)) { goto Done; } @@ -784,17 +760,17 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 refmvCount = 1; } - Done: + Done: // Clamp vectors for (i = 0; i < refmvCount; ++i) { - mvRefList[i].ClampMvRef(ref xd); + mvRefList[i].ClampRef(ref xd); } return refmvCount; } - private static void AppendSub8x8MvsForIdx( + private static void AppendSub8X8ForIdx( ref Vp9Common cm, ref MacroBlockD xd, Span mvRefSearch, @@ -803,46 +779,47 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 int refr, int miRow, int miCol, - ref Mv bestSub8x8) + ref Mv bestSub8X8) { Span mvList = stackalloc Mv[Constants.MaxMvRefCandidates]; ref ModeInfo mi = ref xd.Mi[0].Value; ref Array4 bmi = ref mi.Bmi; - int n; int refmvCount; Debug.Assert(Constants.MaxMvRefCandidates == 2); - refmvCount = DecFindMvRefs(ref cm, ref xd, bMode, mi.RefFrame[refr], mvRefSearch, mvList, miRow, miCol, block, 1); + refmvCount = DecFindRefs(ref cm, ref xd, bMode, mi.RefFrame[refr], mvRefSearch, mvList, miRow, miCol, + block, 1); switch (block) { case 0: - bestSub8x8 = mvList[refmvCount - 1]; + bestSub8X8 = mvList[refmvCount - 1]; break; case 1: case 2: if (bMode == PredictionMode.NearestMv) { - bestSub8x8 = bmi[0].Mv[refr]; + bestSub8X8 = bmi[0].Mv[refr]; } else { - bestSub8x8 = new Mv(); - for (n = 0; n < refmvCount; ++n) + bestSub8X8 = new Mv(); + for (int n = 0; n < refmvCount; ++n) { if (Unsafe.As(ref bmi[0].Mv[refr]) != Unsafe.As(ref mvList[n])) { - bestSub8x8 = mvList[n]; + bestSub8X8 = mvList[n]; break; } } } + break; case 3: if (bMode == PredictionMode.NearestMv) { - bestSub8x8 = bmi[2].Mv[refr]; + bestSub8X8 = bmi[2].Mv[refr]; } else { @@ -851,16 +828,17 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 candidates[1] = bmi[0].Mv[refr]; candidates[2] = mvList[0]; candidates[3] = mvList[1]; - bestSub8x8 = new Mv(); - for (n = 0; n < 2 + Constants.MaxMvRefCandidates; ++n) + bestSub8X8 = new Mv(); + for (int n = 0; n < 2 + Constants.MaxMvRefCandidates; ++n) { if (Unsafe.As(ref bmi[2].Mv[refr]) != Unsafe.As(ref candidates[n])) { - bestSub8x8 = candidates[n]; + bestSub8X8 = candidates[n]; break; } } } + break; default: Debug.Assert(false, "Invalid block index."); @@ -868,19 +846,19 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 } } - private static byte GetModeContext(ref Vp9Common cm, ref MacroBlockD xd, Span mvRefSearch, int miRow, int miCol) + private static byte GetModeContext(ref Vp9Common cm, ref MacroBlockD xd, Span mvRefSearch, int miRow, + int miCol) { - int i; int contextCounter = 0; ref TileInfo tile = ref xd.Tile; // Get mode count from nearest 2 blocks - for (i = 0; i < 2; ++i) + for (int i = 0; i < 2; ++i) { ref Position mvRef = ref mvRefSearch[i]; if (tile.IsInside(miCol, miRow, cm.MiRows, ref mvRef)) { - ref ModeInfo candidate = ref xd.Mi[mvRef.Col + mvRef.Row * xd.MiStride].Value; + ref ModeInfo candidate = ref xd.Mi[mvRef.Col + (mvRef.Row * xd.MiStride)].Value; // Keep counts for entropy encoding. contextCounter += Luts.Mode2Counter[(int)candidate.Mode]; } @@ -898,7 +876,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 ref Reader r) { BlockSize bsize = mi.SbType; - bool allowHP = cm.AllowHighPrecisionMv; + bool allowHp = cm.AllowHighPrecisionMv; Array2 bestRefMvs = new(); int refr, isCompound; byte interModeCtx; @@ -908,19 +886,19 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 isCompound = mi.HasSecondRef() ? 1 : 0; interModeCtx = GetModeContext(ref cm, ref xd, mvRefSearch, miRow, miCol); - if (cm.Seg.IsSegFeatureActive(mi.SegmentId, SegLvlFeatures.SegLvlSkip) != 0) + if (cm.Seg.IsSegFeatureActive(mi.SegmentId, SegLvlFeatures.Skip) != 0) { mi.Mode = PredictionMode.ZeroMv; - if (bsize < BlockSize.Block8x8) + if (bsize < BlockSize.Block8X8) { - xd.ErrorInfo.Value.InternalError(CodecErr.CodecUnsupBitstream, "Invalid usage of segement feature on small blocks"); - + xd.ErrorInfo.Value.InternalError(CodecErr.UnsupBitstream, + "Invalid usage of segement feature on small blocks"); return; } } else { - if (bsize >= BlockSize.Block8x8) + if (bsize >= BlockSize.Block8X8) { mi.Mode = ReadInterMode(ref cm, ref xd, ref r, interModeCtx); } @@ -942,42 +920,47 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 sbyte frame = mi.RefFrame[refr]; int refmvCount; - refmvCount = DecFindMvRefs(ref cm, ref xd, mi.Mode, frame, mvRefSearch, tmpMvs, miRow, miCol, -1, 0); + refmvCount = DecFindRefs(ref cm, ref xd, mi.Mode, frame, mvRefSearch, tmpMvs, miRow, miCol, + -1, 0); - DecFindBestRefMvs(allowHP, tmpMvs, ref bestRefMvs[refr], refmvCount); + DecFindBestRefs(allowHp, tmpMvs, ref bestRefMvs[refr], refmvCount); } } } - mi.InterpFilter = (cm.InterpFilter == Constants.Switchable) ? ReadSwitchableInterpFilter(ref cm, ref xd, ref r) : cm.InterpFilter; + mi.InterpFilter = cm.InterpFilter == Constants.Switchable + ? ReadSwitchableInterpFilter(ref cm, ref xd, ref r) + : cm.InterpFilter; - if (bsize < BlockSize.Block8x8) + if (bsize < BlockSize.Block8X8) { int num4X4W = 1 << xd.BmodeBlocksWl; int num4X4H = 1 << xd.BmodeBlocksHl; int idx, idy; PredictionMode bMode = 0; - Array2 bestSub8x8 = new(); + Array2 bestSub8X8 = new(); const uint InvalidMv = 0x80008000; // Initialize the 2nd element as even though it won't be used meaningfully // if isCompound is false. - Unsafe.As(ref bestSub8x8[1]) = InvalidMv; + Unsafe.As(ref bestSub8X8[1]) = InvalidMv; for (idy = 0; idy < 2; idy += num4X4H) { for (idx = 0; idx < 2; idx += num4X4W) { - int j = idy * 2 + idx; + int j = (idy * 2) + idx; bMode = ReadInterMode(ref cm, ref xd, ref r, interModeCtx); if (bMode == PredictionMode.NearestMv || bMode == PredictionMode.NearMv) { for (refr = 0; refr < 1 + isCompound; ++refr) { - AppendSub8x8MvsForIdx(ref cm, ref xd, mvRefSearch, bMode, j, refr, miRow, miCol, ref bestSub8x8[refr]); + AppendSub8X8ForIdx(ref cm, ref xd, mvRefSearch, bMode, j, refr, miRow, miCol, + ref bestSub8X8[refr]); } } - if (!AssignMv(ref cm, ref xd, bMode, ref mi.Bmi[j].Mv, ref bestRefMvs, ref bestSub8x8, isCompound, allowHP, ref r)) + if (!Assign(ref cm, ref xd, bMode, ref mi.Bmi[j].Mv, ref bestRefMvs, ref bestSub8X8, + isCompound, allowHp, ref r)) { xd.Corrupted |= true; break; @@ -997,11 +980,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 mi.Mode = bMode; - CopyMvPair(ref mi.Mv, ref mi.Bmi[3].Mv); + CopyPair(ref mi.Mv, ref mi.Bmi[3].Mv); } else { - xd.Corrupted |= !AssignMv(ref cm, ref xd, mi.Mode, ref mi.Mv, ref bestRefMvs, ref bestRefMvs, isCompound, allowHP, ref r); + xd.Corrupted |= !Assign(ref cm, ref xd, mi.Mode, ref mi.Mv, ref bestRefMvs, ref bestRefMvs, + isCompound, allowHp, ref r); } } @@ -1045,7 +1029,6 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 } Debug.Assert(b == 1 || b == 3); - return curMi.Value.Bmi[b - 1].Mode; } @@ -1062,7 +1045,6 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 } Debug.Assert(b == 2 || b == 3); - return curMi.Value.Bmi[b - 2].Mode; } @@ -1075,7 +1057,6 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { PredictionMode above = AboveBlockMode(mi, aboveMi, block); PredictionMode left = LeftBlockMode(mi, leftMi, block); - return fc.KfYModeProb[(int)above][(int)left].AsSpan(); } @@ -1092,8 +1073,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 Ptr aboveMi = xd.AboveMi; Ptr leftMi = xd.LeftMi; BlockSize bsize = mi.Value.SbType; - int i; - int miOffset = miRow * cm.MiCols + miCol; + + int miOffset = (miRow * cm.MiCols) + miCol; mi.Value.SegmentId = (sbyte)ReadIntraSegmentId(ref cm, miOffset, xMis, yMis, ref r); mi.Value.Skip = (sbyte)ReadSkip(ref cm, ref xd, mi.Value.SegmentId, ref r); @@ -1103,8 +1084,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 switch (bsize) { - case BlockSize.Block4x4: - for (i = 0; i < 4; ++i) + case BlockSize.Block4X4: + for (int i = 0; i < 4; ++i) { mi.Value.Bmi[i].Mode = ReadIntraMode(ref r, GetYModeProbs(ref cm.Fc.Value, mi, aboveMi, leftMi, i)); @@ -1112,13 +1093,13 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 mi.Value.Mode = mi.Value.Bmi[3].Mode; break; - case BlockSize.Block4x8: + case BlockSize.Block4X8: mi.Value.Bmi[0].Mode = mi.Value.Bmi[2].Mode = ReadIntraMode(ref r, GetYModeProbs(ref cm.Fc.Value, mi, aboveMi, leftMi, 0)); mi.Value.Bmi[1].Mode = mi.Value.Bmi[3].Mode = mi.Value.Mode = ReadIntraMode(ref r, GetYModeProbs(ref cm.Fc.Value, mi, aboveMi, leftMi, 1)); break; - case BlockSize.Block8x4: + case BlockSize.Block8X4: mi.Value.Bmi[0].Mode = mi.Value.Bmi[1].Mode = ReadIntraMode(ref r, GetYModeProbs(ref cm.Fc.Value, mi, aboveMi, leftMi, 0)); mi.Value.Bmi[2].Mode = mi.Value.Bmi[3].Mode = mi.Value.Mode = @@ -1149,8 +1130,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 ref Reader r = ref twd.BitReader; ref MacroBlockD xd = ref twd.Xd; ref ModeInfo mi = ref xd.Mi[0].Value; - ArrayPtr frameMvs = cm.CurFrameMvs.Slice(miRow * cm.MiCols + miCol); - int w, h; + ArrayPtr frameMvs = cm.CurFrameMvs.Slice((miRow * cm.MiCols) + miCol); if (cm.FrameIsIntraOnly()) { @@ -1160,17 +1140,18 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { ReadInterFrameModeInfo(ref cm, ref xd, miRow, miCol, ref r, xMis, yMis); - for (h = 0; h < yMis; ++h) + for (int h = 0; h < yMis; ++h) { - for (w = 0; w < xMis; ++w) + for (int w = 0; w < xMis; ++w) { ref MvRef mv = ref frameMvs[w]; CopyRefFramePair(ref mv.RefFrame, ref mi.RefFrame); - CopyMvPair(ref mv.Mv, ref mi.Mv); + CopyPair(ref mv.Mv, ref mi.Mv); } + frameMvs = frameMvs.Slice(cm.MiCols); } } } } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Decoder.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Decoder.cs index 57057d5f9..009611b27 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Decoder.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Decoder.cs @@ -1,4 +1,4 @@ -using Ryujinx.Common.Memory; +using Ryujinx.Common.Memory; using Ryujinx.Graphics.Nvdec.Vp9.Common; using Ryujinx.Graphics.Nvdec.Vp9.Types; using Ryujinx.Graphics.Video; @@ -12,15 +12,15 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 private readonly MemoryAllocator _allocator = new(); - public ISurface CreateSurface(int width, int height) => new Surface(width, height); - - private static ReadOnlySpan LiteralToFilter => new byte[] + public ISurface CreateSurface(int width, int height) { - Constants.EightTapSmooth, - Constants.EightTap, - Constants.EightTapSharp, - Constants.Bilinear, - }; + return new Surface(width, height); + } + + private static ReadOnlySpan LiteralToFilter => + [ + Constants.EightTapSmooth, Constants.EightTap, Constants.EightTapSharp, Constants.Bilinear + ]; public unsafe bool Decode( ref Vp9PictureInfo pictureInfo, @@ -29,25 +29,24 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 ReadOnlySpan mvsIn, Span mvsOut) { - Vp9Common cm = new() - { - FrameType = pictureInfo.IsKeyFrame ? FrameType.KeyFrame : FrameType.InterFrame, - IntraOnly = pictureInfo.IntraOnly, + Vp9Common cm = new(); - Width = output.Width, - Height = output.Height, - SubsamplingX = 1, - SubsamplingY = 1, + cm.FrameType = pictureInfo.IsKeyFrame ? FrameType.KeyFrame : FrameType.InterFrame; + cm.IntraOnly = pictureInfo.IntraOnly; - UsePrevFrameMvs = pictureInfo.UsePrevInFindMvRefs, + cm.Width = output.Width; + cm.Height = output.Height; + cm.SubsamplingX = 1; + cm.SubsamplingY = 1; - RefFrameSignBias = pictureInfo.RefFrameSignBias, + cm.UsePrevFrameMvs = pictureInfo.UsePrevInFindMvRefs; - BaseQindex = pictureInfo.BaseQIndex, - YDcDeltaQ = pictureInfo.YDcDeltaQ, - UvAcDeltaQ = pictureInfo.UvAcDeltaQ, - UvDcDeltaQ = pictureInfo.UvDcDeltaQ, - }; + cm.RefFrameSignBias = pictureInfo.RefFrameSignBias; + + cm.BaseQindex = pictureInfo.BaseQIndex; + cm.YDcDeltaQ = pictureInfo.YDcDeltaQ; + cm.UvAcDeltaQ = pictureInfo.UvAcDeltaQ; + cm.UvDcDeltaQ = pictureInfo.UvDcDeltaQ; cm.Mb.Lossless = pictureInfo.Lossless; cm.Mb.Bd = 8; @@ -68,6 +67,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 cm.CompFixedRef = pictureInfo.CompFixedRef; cm.CompVarRef = pictureInfo.CompVarRef; + cm.BitDepth = BitDepth.Bits8; + cm.Log2TileCols = pictureInfo.Log2TileCols; cm.Log2TileRows = pictureInfo.Log2TileRows; @@ -78,6 +79,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 cm.Seg.FeatureMask = pictureInfo.SegmentFeatureEnable; cm.Seg.FeatureData = pictureInfo.SegmentFeatureData; + cm.Lf.FilterLevel = pictureInfo.LoopFilterLevel; + cm.Lf.SharpnessLevel = pictureInfo.LoopFilterSharpnessLevel; cm.Lf.ModeRefDeltaEnabled = pictureInfo.ModeRefDeltaEnabled; cm.Lf.RefDeltas = pictureInfo.RefDeltas; cm.Lf.ModeDeltas = pictureInfo.ModeDeltas; @@ -105,7 +108,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 cm.SetupSegmentationDequant(); cm.SetupScaleFactors(); - SetMvs(ref cm, mvsIn); + cm.SetMvs(mvsIn); + + if (cm.Lf.FilterLevel != 0 && cm.SkipLoopFilter == 0) + { + LoopFilter.LoopFilterFrameInit(ref cm, cm.Lf.FilterLevel); + } fixed (byte* dataPtr = bitstream) { @@ -114,10 +122,27 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 if (maxThreads > 1 && tileRows == 1 && tileCols > 1) { DecodeFrame.DecodeTilesMt(ref cm, new ArrayPtr(dataPtr, bitstream.Length), maxThreads); + + LoopFilter.LoopFilterFrameMt( + ref cm.Mb.CurBuf, + ref cm, + ref cm.Mb, + cm.Lf.FilterLevel, + false, + false, + maxThreads); } else { DecodeFrame.DecodeTiles(ref cm, new ArrayPtr(dataPtr, bitstream.Length)); + + LoopFilter.LoopFilterFrame( + ref cm.Mb.CurBuf, + ref cm, + ref cm.Mb, + cm.Lf.FilterLevel, + false, + false); } } catch (InternalErrorException) @@ -126,7 +151,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 } } - GetMvs(ref cm, mvsOut); + cm.GetMvs(mvsOut); cm.FreeTileWorkerData(_allocator); cm.FreeContextBuffers(_allocator); @@ -134,48 +159,9 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 return true; } - private static void SetMvs(ref Vp9Common cm, ReadOnlySpan mvs) + public void Dispose() { - if (mvs.Length > cm.PrevFrameMvs.Length) - { - throw new ArgumentException($"Size mismatch, expected: {cm.PrevFrameMvs.Length}, but got: {mvs.Length}."); - } - - for (int i = 0; i < mvs.Length; i++) - { - ref var mv = ref cm.PrevFrameMvs[i]; - - mv.Mv[0].Row = mvs[i].Mvs[0].Row; - mv.Mv[0].Col = mvs[i].Mvs[0].Col; - mv.Mv[1].Row = mvs[i].Mvs[1].Row; - mv.Mv[1].Col = mvs[i].Mvs[1].Col; - - mv.RefFrame[0] = (sbyte)mvs[i].RefFrames[0]; - mv.RefFrame[1] = (sbyte)mvs[i].RefFrames[1]; - } + _allocator.Dispose(); } - - private static void GetMvs(ref Vp9Common cm, Span mvs) - { - if (mvs.Length > cm.CurFrameMvs.Length) - { - throw new ArgumentException($"Size mismatch, expected: {cm.CurFrameMvs.Length}, but got: {mvs.Length}."); - } - - for (int i = 0; i < mvs.Length; i++) - { - ref var mv = ref cm.CurFrameMvs[i]; - - mvs[i].Mvs[0].Row = mv.Mv[0].Row; - mvs[i].Mvs[0].Col = mv.Mv[0].Col; - mvs[i].Mvs[1].Row = mv.Mv[1].Row; - mvs[i].Mvs[1].Col = mv.Mv[1].Col; - - mvs[i].RefFrames[0] = mv.RefFrame[0]; - mvs[i].RefFrames[1] = mv.RefFrame[1]; - } - } - - public void Dispose() => _allocator.Dispose(); } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Detokenize.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Detokenize.cs index c255f7486..092d298dc 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Detokenize.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Detokenize.cs @@ -1,4 +1,4 @@ -using Ryujinx.Common.Memory; +using Ryujinx.Common.Memory; using Ryujinx.Graphics.Nvdec.Vp9.Dsp; using Ryujinx.Graphics.Nvdec.Vp9.Types; using Ryujinx.Graphics.Video; @@ -19,24 +19,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { const int MaxNeighbors = 2; - return (1 + tokenCache[neighbors[MaxNeighbors * c + 0]] + tokenCache[neighbors[MaxNeighbors * c + 1]]) >> 1; - } - - private static int ReadCoeff( - ref Reader r, - ReadOnlySpan probs, - int n, - ref ulong value, - ref int count, - ref uint range) - { - int i, val = 0; - for (i = 0; i < n; ++i) - { - val = (val << 1) | r.ReadBool(probs[i], ref value, ref count, ref range); - } - - return val; + return (1 + tokenCache[neighbors[(MaxNeighbors * c) + 0]] + + tokenCache[neighbors[(MaxNeighbors * c) + 1]]) >> 1; } private static int DecodeCoefs( @@ -58,13 +42,15 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 ref Array6>> coefProbs = ref fc.CoefProbs[(int)txSize][(int)type][refr]; Span tokenCache = stackalloc byte[32 * 32]; ReadOnlySpan bandTranslate = Luts.GetBandTranslate(txSize); - int dqShift = (txSize == TxSize.Tx32x32) ? 1 : 0; + int dqShift = txSize == TxSize.Tx32X32 ? 1 : 0; int v; short dqv = dq[0]; - ReadOnlySpan cat6Prob = (xd.Bd == 12) - ? Luts.Vp9Cat6ProbHigh12 - : (xd.Bd == 10) ? Luts.Vp9Cat6ProbHigh12[2..] : Luts.Vp9Cat6Prob; - int cat6Bits = (xd.Bd == 12) ? 18 : (xd.Bd == 10) ? 16 : 14; + ReadOnlySpan cat6Prob = xd.Bd == 12 + ? Luts.Cat6ProbHigh12 + : xd.Bd == 10 + ? Luts.Cat6ProbHigh12.Slice(2) + : Luts.Cat6Prob; + int cat6Bits = xd.Bd == 12 ? 18 : xd.Bd == 10 ? 16 : 14; // Keep value, range, and count as locals. The compiler produces better // results with the locals than using r directly. ulong value = r.Value; @@ -75,7 +61,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { int val = -1; band = bandTranslate[0]; - bandTranslate = bandTranslate[1..]; + bandTranslate = bandTranslate.Slice(1); ref Array3 prob = ref coefProbs[band][ctx]; if (!xd.Counts.IsNull) { @@ -107,18 +93,18 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 r.Value = value; r.Range = range; r.Count = count; - return c; // Zero tokens at the end (no eob token) } + ctx = GetCoefContext(nb, tokenCache, c); band = bandTranslate[0]; - bandTranslate = bandTranslate[1..]; + bandTranslate = bandTranslate.Slice(1); prob = ref coefProbs[band][ctx]; } if (r.ReadBool(prob[OneContextNode], ref value, ref count, ref range) != 0) { - ReadOnlySpan p = Luts.Vp9Pareto8Full[prob[Constants.PivotNode] - 1]; + ReadOnlySpan p = Luts.Pareto8Full[prob[Constants.PivotNode] - 1]; if (!xd.Counts.IsNull) { ++counts.Coef[(int)txSize][(int)type][refr][band][ctx][Constants.TwoToken]; @@ -133,20 +119,24 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { if (r.ReadBool(p[7], ref value, ref count, ref range) != 0) { - val = Constants.Cat6MinVal + ReadCoeff(ref r, cat6Prob, cat6Bits, ref value, ref count, ref range); + val = Constants.Cat6MinVal + r.ReadCoeff(cat6Prob, cat6Bits, ref value, + ref count, ref range); } else { - val = Constants.Cat5MinVal + ReadCoeff(ref r, Luts.Vp9Cat5Prob, 5, ref value, ref count, ref range); + val = Constants.Cat5MinVal + r.ReadCoeff(Luts.Cat5Prob, 5, ref value, + ref count, ref range); } } else if (r.ReadBool(p[6], ref value, ref count, ref range) != 0) { - val = Constants.Cat4MinVal + ReadCoeff(ref r, Luts.Vp9Cat4Prob, 4, ref value, ref count, ref range); + val = Constants.Cat4MinVal + r.ReadCoeff(Luts.Cat4Prob, 4, ref value, ref count, + ref range); } else { - val = Constants.Cat3MinVal + ReadCoeff(ref r, Luts.Vp9Cat3Prob, 3, ref value, ref count, ref range); + val = Constants.Cat3MinVal + r.ReadCoeff(Luts.Cat3Prob, 3, ref value, ref count, + ref range); } } else @@ -154,13 +144,16 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 tokenCache[scan[c]] = 4; if (r.ReadBool(p[4], ref value, ref count, ref range) != 0) { - val = Constants.Cat2MinVal + ReadCoeff(ref r, Luts.Vp9Cat2Prob, 2, ref value, ref count, ref range); + val = Constants.Cat2MinVal + r.ReadCoeff(Luts.Cat2Prob, 2, ref value, ref count, + ref range); } else { - val = Constants.Cat1MinVal + ReadCoeff(ref r, Luts.Vp9Cat1Prob, 1, ref value, ref count, ref range); + val = Constants.Cat1MinVal + r.ReadCoeff(Luts.Cat1Prob, 1, ref value, ref count, + ref range); } } + // Val may use 18-bits v = (int)(((long)val * dqv) >> dqShift); } @@ -188,7 +181,9 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 tokenCache[scan[c]] = 1; v = dqv >> dqShift; } - dqcoeff[scan[c]] = (int)HighbdCheckRange(r.ReadBool(128, ref value, ref count, ref range) != 0 ? -v : v, xd.Bd); + + dqcoeff[scan[c]] = (int)HighbdCheckRange(r.ReadBool(128, ref value, ref count, ref range) != 0 ? -v : v, + xd.Bd); ++c; ctx = GetCoefContext(nb, tokenCache, c); dqv = dq[1]; @@ -197,11 +192,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 r.Value = value; r.Range = range; r.Count = count; - return c; } - private static void GetCtxShift(ref MacroBlockD xd, ref int ctxShiftA, ref int ctxShiftL, int x, int y, uint txSizeInBlocks) + private static void GetCtxShift(ref MacroBlockD xd, ref int ctxShiftA, ref int ctxShiftL, int x, int y, + uint txSizeInBlocks) { if (xd.MaxBlocksWide != 0) { @@ -210,6 +205,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 ctxShiftA = (int)(txSizeInBlocks - (xd.MaxBlocksWide - x)) * 8; } } + if (xd.MaxBlocksHigh != 0) { if (txSizeInBlocks + y > xd.MaxBlocksHigh) @@ -238,15 +234,15 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 ref MacroBlockDPlane pd = ref xd.Plane[plane]; ref Array2 dequant = ref pd.SegDequant[segId]; int eob; - Span a = pd.AboveContext.AsSpan()[x..]; - Span l = pd.LeftContext.AsSpan()[y..]; + Span a = pd.AboveContext.AsSpan().Slice(x); + Span l = pd.LeftContext.AsSpan().Slice(y); int ctx; int ctxShiftA = 0; int ctxShiftL = 0; switch (txSize) { - case TxSize.Tx4x4: + case TxSize.Tx4X4: ctx = a[0] != 0 ? 1 : 0; ctx += l[0] != 0 ? 1 : 0; eob = DecodeCoefs( @@ -261,8 +257,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 ref r); a[0] = l[0] = (sbyte)(eob > 0 ? 1 : 0); break; - case TxSize.Tx8x8: - GetCtxShift(ref xd, ref ctxShiftA, ref ctxShiftL, x, y, 1 << (int)TxSize.Tx8x8); + case TxSize.Tx8X8: + GetCtxShift(ref xd, ref ctxShiftA, ref ctxShiftL, x, y, 1 << (int)TxSize.Tx8X8); ctx = MemoryMarshal.Cast(a)[0] != 0 ? 1 : 0; ctx += MemoryMarshal.Cast(l)[0] != 0 ? 1 : 0; eob = DecodeCoefs( @@ -278,8 +274,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 MemoryMarshal.Cast(a)[0] = (ushort)((eob > 0 ? 0x0101 : 0) >> ctxShiftA); MemoryMarshal.Cast(l)[0] = (ushort)((eob > 0 ? 0x0101 : 0) >> ctxShiftL); break; - case TxSize.Tx16x16: - GetCtxShift(ref xd, ref ctxShiftA, ref ctxShiftL, x, y, 1 << (int)TxSize.Tx16x16); + case TxSize.Tx16X16: + GetCtxShift(ref xd, ref ctxShiftA, ref ctxShiftL, x, y, 1 << (int)TxSize.Tx16X16); ctx = MemoryMarshal.Cast(a)[0] != 0 ? 1 : 0; ctx += MemoryMarshal.Cast(l)[0] != 0 ? 1 : 0; eob = DecodeCoefs( @@ -295,8 +291,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 MemoryMarshal.Cast(a)[0] = (uint)((eob > 0 ? 0x01010101 : 0) >> ctxShiftA); MemoryMarshal.Cast(l)[0] = (uint)((eob > 0 ? 0x01010101 : 0) >> ctxShiftL); break; - case TxSize.Tx32x32: - GetCtxShift(ref xd, ref ctxShiftA, ref ctxShiftL, x, y, 1 << (int)TxSize.Tx32x32); + case TxSize.Tx32X32: + GetCtxShift(ref xd, ref ctxShiftA, ref ctxShiftL, x, y, 1 << (int)TxSize.Tx32X32); // NOTE: Casting to ulong here is safe because the default memory // alignment is at least 8 bytes and the Tx32x32 is aligned on 8 byte // boundaries. @@ -324,4 +320,4 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 return eob; } } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/Convolve.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/Convolve.cs index 9e279dd21..8cea09aec 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/Convolve.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/Convolve.cs @@ -1,4 +1,4 @@ -using Ryujinx.Common.Memory; +using Ryujinx.Common.Memory; using Ryujinx.Graphics.Nvdec.Vp9.Common; using System.Diagnostics; using System.Runtime.CompilerServices; @@ -75,17 +75,16 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp Vector128 zero = Vector128.Zero; Vector128 const64 = Vector128.Create(64); - ulong x, y; - src -= SubpelTaps / 2 - 1; + src -= (SubpelTaps / 2) - 1; fixed (Array8* xFilter = xFilters) { - Vector128 vfilter = Sse2.LoadVector128((short*)xFilter + (uint)(x0Q4 & SubpelMask) * 8); + Vector128 vfilter = Sse2.LoadVector128((short*)xFilter + ((uint)(x0Q4 & SubpelMask) * 8)); - for (y = 0; y < (uint)h; ++y) + for (ulong y = 0; y < (uint)h; ++y) { ulong srcOffset = (uint)x0Q4 >> SubpelBits; - for (x = 0; x < (uint)w; x += 4) + for (ulong x = 0; x < (uint)w; x += 4) { Vector128 vsrc0 = Sse41.ConvertToVector128Int16(&src[srcOffset + x]); Vector128 vsrc1 = Sse41.ConvertToVector128Int16(&src[srcOffset + x + 1]); @@ -94,8 +93,10 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp Vector128 sum0123 = MultiplyAddAdjacent(vsrc0, vsrc1, vsrc2, vsrc3, vfilter, zero); - Sse.StoreScalar((float*)&dst[x], PackUnsignedSaturate(RoundShift(sum0123, const64), zero).AsSingle()); + Sse.StoreScalar((float*)&dst[x], + PackUnsignedSaturate(RoundShift(sum0123, const64), zero).AsSingle()); } + src += srcStride; dst += dstStride; } @@ -117,22 +118,20 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp if (Sse41.IsSupported && UseIntrinsics && xStepQ4 == 1 << SubpelBits) { ConvolveHorizSse41(src, srcStride, dst, dstStride, xFilters, x0Q4, w, h); - return; } - int x, y; - src -= SubpelTaps / 2 - 1; + src -= (SubpelTaps / 2) - 1; - for (y = 0; y < h; ++y) + for (int y = 0; y < h; ++y) { int xQ4 = x0Q4; - for (x = 0; x < w; ++x) + for (int x = 0; x < w; ++x) { byte* srcX = &src[xQ4 >> SubpelBits]; ref Array8 xFilter = ref xFilters[xQ4 & SubpelMask]; - int k, sum = 0; - for (k = 0; k < SubpelTaps; ++k) + int sum = 0; + for (int k = 0; k < SubpelTaps; ++k) { sum += srcX[k] * xFilter[k]; } @@ -140,6 +139,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp dst[x] = BitUtils.ClipPixel(BitUtils.RoundPowerOfTwo(sum, FilterBits)); xQ4 += xStepQ4; } + src += srcStride; dst += dstStride; } @@ -156,25 +156,26 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp int w, int h) { - int x, y; - src -= SubpelTaps / 2 - 1; + src -= (SubpelTaps / 2) - 1; - for (y = 0; y < h; ++y) + for (int y = 0; y < h; ++y) { int xQ4 = x0Q4; - for (x = 0; x < w; ++x) + for (int x = 0; x < w; ++x) { byte* srcX = &src[xQ4 >> SubpelBits]; ref Array8 xFilter = ref xFilters[xQ4 & SubpelMask]; - int k, sum = 0; - for (k = 0; k < SubpelTaps; ++k) + int sum = 0; + for (int k = 0; k < SubpelTaps; ++k) { sum += srcX[k] * xFilter[k]; } - dst[x] = (byte)BitUtils.RoundPowerOfTwo(dst[x] + BitUtils.ClipPixel(BitUtils.RoundPowerOfTwo(sum, FilterBits)), 1); + dst[x] = (byte)BitUtils.RoundPowerOfTwo( + dst[x] + BitUtils.ClipPixel(BitUtils.RoundPowerOfTwo(sum, FilterBits)), 1); xQ4 += xStepQ4; } + src += srcStride; dst += dstStride; } @@ -203,18 +204,17 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp srcStride * 6, srcStride * 7); - ulong x, y; - src -= srcStride * (SubpelTaps / 2 - 1); + src -= srcStride * ((SubpelTaps / 2) - 1); fixed (Array8* yFilter = yFilters) { - Vector128 vfilter = Sse2.LoadVector128((short*)yFilter + (uint)(y0Q4 & SubpelMask) * 8); + Vector128 vfilter = Sse2.LoadVector128((short*)yFilter + ((uint)(y0Q4 & SubpelMask) * 8)); ulong srcBaseY = (uint)y0Q4 >> SubpelBits; - for (y = 0; y < (uint)h; ++y) + for (ulong y = 0; y < (uint)h; ++y) { ulong srcOffset = (srcBaseY + y) * (uint)srcStride; - for (x = 0; x < (uint)w; x += 4) + for (ulong x = 0; x < (uint)w; x += 4) { Vector256 vsrc = Avx2.GatherVector256((uint*)&src[srcOffset + x], indices, 1).AsInt32(); @@ -240,8 +240,10 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp Vector128 sum0123 = MultiplyAddAdjacent(vsrc0, vsrc1, vsrc2, vsrc3, vfilter, zero); - Sse.StoreScalar((float*)&dst[x], PackUnsignedSaturate(RoundShift(sum0123, const64), zero).AsSingle()); + Sse.StoreScalar((float*)&dst[x], + PackUnsignedSaturate(RoundShift(sum0123, const64), zero).AsSingle()); } + dst += dstStride; } } @@ -262,22 +264,20 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp if (Avx2.IsSupported && UseIntrinsics && yStepQ4 == 1 << SubpelBits) { ConvolveVertAvx2(src, srcStride, dst, dstStride, yFilters, y0Q4, w, h); - return; } - int x, y; - src -= srcStride * (SubpelTaps / 2 - 1); + src -= srcStride * ((SubpelTaps / 2) - 1); - for (x = 0; x < w; ++x) + for (int x = 0; x < w; ++x) { int yQ4 = y0Q4; - for (y = 0; y < h; ++y) + for (int y = 0; y < h; ++y) { byte* srcY = &src[(yQ4 >> SubpelBits) * srcStride]; ref Array8 yFilter = ref yFilters[yQ4 & SubpelMask]; - int k, sum = 0; - for (k = 0; k < SubpelTaps; ++k) + int sum = 0; + for (int k = 0; k < SubpelTaps; ++k) { sum += srcY[k * srcStride] * yFilter[k]; } @@ -285,6 +285,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp dst[y * dstStride] = BitUtils.ClipPixel(BitUtils.RoundPowerOfTwo(sum, FilterBits)); yQ4 += yStepQ4; } + ++src; ++dst; } @@ -301,18 +302,17 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp int w, int h) { - int x, y; - src -= srcStride * (SubpelTaps / 2 - 1); + src -= srcStride * ((SubpelTaps / 2) - 1); - for (x = 0; x < w; ++x) + for (int x = 0; x < w; ++x) { int yQ4 = y0Q4; - for (y = 0; y < h; ++y) + for (int y = 0; y < h; ++y) { byte* srcY = &src[(yQ4 >> SubpelBits) * srcStride]; ref Array8 yFilter = ref yFilters[yQ4 & SubpelMask]; - int k, sum = 0; - for (k = 0; k < SubpelTaps; ++k) + int sum = 0; + for (int k = 0; k < SubpelTaps; ++k) { sum += srcY[k * srcStride] * yFilter[k]; } @@ -321,6 +321,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp dst[y * dstStride] + BitUtils.ClipPixel(BitUtils.RoundPowerOfTwo(sum, FilterBits)), 1); yQ4 += yStepQ4; } + ++src; ++dst; } @@ -420,15 +421,16 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp // ==> yStepQ4 = 64. Since w and h are at most 16, the temp buffer is still // big enough. byte* temp = stackalloc byte[64 * 135]; - int intermediateHeight = (((h - 1) * yStepQ4 + y0Q4) >> SubpelBits) + SubpelTaps; + int intermediateHeight = ((((h - 1) * yStepQ4) + y0Q4) >> SubpelBits) + SubpelTaps; Debug.Assert(w <= 64); Debug.Assert(h <= 64); Debug.Assert(yStepQ4 <= 32 || (yStepQ4 <= 64 && h <= 32)); Debug.Assert(xStepQ4 <= 64); - ConvolveHoriz(src - srcStride * (SubpelTaps / 2 - 1), srcStride, temp, 64, filter, x0Q4, xStepQ4, w, intermediateHeight); - ConvolveVert(temp + 64 * (SubpelTaps / 2 - 1), 64, dst, dstStride, filter, y0Q4, yStepQ4, w, h); + ConvolveHoriz(src - (srcStride * ((SubpelTaps / 2) - 1)), srcStride, temp, 64, filter, x0Q4, xStepQ4, w, + intermediateHeight); + ConvolveVert(temp + (64 * ((SubpelTaps / 2) - 1)), 64, dst, dstStride, filter, y0Q4, yStepQ4, w, h); } public static unsafe void Convolve8Avg( @@ -489,11 +491,9 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp int w, int h) { - int x, y; - - for (y = 0; y < h; ++y) + for (int y = 0; y < h; ++y) { - for (x = 0; x < w; ++x) + for (int x = 0; x < w; ++x) { dst[x] = (byte)BitUtils.RoundPowerOfTwo(dst[x] + src[x], 1); } @@ -611,18 +611,17 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp int h, int bd) { - int x, y; - src -= SubpelTaps / 2 - 1; + src -= (SubpelTaps / 2) - 1; - for (y = 0; y < h; ++y) + for (int y = 0; y < h; ++y) { int xQ4 = x0Q4; - for (x = 0; x < w; ++x) + for (int x = 0; x < w; ++x) { ushort* srcX = &src[xQ4 >> SubpelBits]; ref Array8 xFilter = ref xFilters[xQ4 & SubpelMask]; - int k, sum = 0; - for (k = 0; k < SubpelTaps; ++k) + int sum = 0; + for (int k = 0; k < SubpelTaps; ++k) { sum += srcX[k] * xFilter[k]; } @@ -630,6 +629,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp dst[x] = BitUtils.ClipPixelHighbd(BitUtils.RoundPowerOfTwo(sum, FilterBits), bd); xQ4 += xStepQ4; } + src += srcStride; dst += dstStride; } @@ -647,25 +647,26 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp int h, int bd) { - int x, y; - src -= SubpelTaps / 2 - 1; + src -= (SubpelTaps / 2) - 1; - for (y = 0; y < h; ++y) + for (int y = 0; y < h; ++y) { int xQ4 = x0Q4; - for (x = 0; x < w; ++x) + for (int x = 0; x < w; ++x) { ushort* srcX = &src[xQ4 >> SubpelBits]; ref Array8 xFilter = ref xFilters[xQ4 & SubpelMask]; - int k, sum = 0; - for (k = 0; k < SubpelTaps; ++k) + int sum = 0; + for (int k = 0; k < SubpelTaps; ++k) { sum += srcX[k] * xFilter[k]; } - dst[x] = (ushort)BitUtils.RoundPowerOfTwo(dst[x] + BitUtils.ClipPixelHighbd(BitUtils.RoundPowerOfTwo(sum, FilterBits), bd), 1); + dst[x] = (ushort)BitUtils.RoundPowerOfTwo( + dst[x] + BitUtils.ClipPixelHighbd(BitUtils.RoundPowerOfTwo(sum, FilterBits), bd), 1); xQ4 += xStepQ4; } + src += srcStride; dst += dstStride; } @@ -683,18 +684,17 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp int h, int bd) { - int x, y; - src -= srcStride * (SubpelTaps / 2 - 1); + src -= srcStride * ((SubpelTaps / 2) - 1); - for (x = 0; x < w; ++x) + for (int x = 0; x < w; ++x) { int yQ4 = y0Q4; - for (y = 0; y < h; ++y) + for (int y = 0; y < h; ++y) { ushort* srcY = &src[(yQ4 >> SubpelBits) * srcStride]; ref Array8 yFilter = ref yFilters[yQ4 & SubpelMask]; - int k, sum = 0; - for (k = 0; k < SubpelTaps; ++k) + int sum = 0; + for (int k = 0; k < SubpelTaps; ++k) { sum += srcY[k * srcStride] * yFilter[k]; } @@ -702,6 +702,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp dst[y * dstStride] = BitUtils.ClipPixelHighbd(BitUtils.RoundPowerOfTwo(sum, FilterBits), bd); yQ4 += yStepQ4; } + ++src; ++dst; } @@ -719,26 +720,27 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp int h, int bd) { - int x, y; - src -= srcStride * (SubpelTaps / 2 - 1); + src -= srcStride * ((SubpelTaps / 2) - 1); - for (x = 0; x < w; ++x) + for (int x = 0; x < w; ++x) { int yQ4 = y0Q4; - for (y = 0; y < h; ++y) + for (int y = 0; y < h; ++y) { ushort* srcY = &src[(yQ4 >> SubpelBits) * srcStride]; ref Array8 yFilter = ref yFilters[yQ4 & SubpelMask]; - int k, sum = 0; - for (k = 0; k < SubpelTaps; ++k) + int sum = 0; + for (int k = 0; k < SubpelTaps; ++k) { sum += srcY[k * srcStride] * yFilter[k]; } dst[y * dstStride] = (ushort)BitUtils.RoundPowerOfTwo( - dst[y * dstStride] + BitUtils.ClipPixelHighbd(BitUtils.RoundPowerOfTwo(sum, FilterBits), bd), 1); + dst[y * dstStride] + BitUtils.ClipPixelHighbd(BitUtils.RoundPowerOfTwo(sum, FilterBits), bd), + 1); yQ4 += yStepQ4; } + ++src; ++dst; } @@ -771,15 +773,17 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp // --Require an additional SubpelTaps rows for the 8-tap filter tails. // --((64 - 1) * 32 + 15) >> 4 + 8 = 135. ushort* temp = stackalloc ushort[64 * 135]; - int intermediateHeight = (((h - 1) * yStepQ4 + y0Q4) >> SubpelBits) + SubpelTaps; + int intermediateHeight = ((((h - 1) * yStepQ4) + y0Q4) >> SubpelBits) + SubpelTaps; Debug.Assert(w <= 64); Debug.Assert(h <= 64); Debug.Assert(yStepQ4 <= 32); Debug.Assert(xStepQ4 <= 32); - HighbdConvolveHoriz(src - srcStride * (SubpelTaps / 2 - 1), srcStride, temp, 64, filter, x0Q4, xStepQ4, w, intermediateHeight, bd); - HighbdConvolveVert(temp + 64 * (SubpelTaps / 2 - 1), 64, dst, dstStride, filter, y0Q4, yStepQ4, w, h, bd); + HighbdConvolveHoriz(src - (srcStride * ((SubpelTaps / 2) - 1)), srcStride, temp, 64, filter, x0Q4, xStepQ4, + w, intermediateHeight, bd); + HighbdConvolveVert(temp + (64 * ((SubpelTaps / 2) - 1)), 64, dst, dstStride, filter, y0Q4, yStepQ4, w, h, + bd); } public static unsafe void HighbdConvolve8Horiz( @@ -928,11 +932,9 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp int h, int bd) { - int x, y; - - for (y = 0; y < h; ++y) + for (int y = 0; y < h; ++y) { - for (x = 0; x < w; ++x) + for (int x = 0; x < w; ++x) { dst[x] = (ushort)BitUtils.RoundPowerOfTwo(dst[x] + src[x], 1); } @@ -942,4 +944,4 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp } } } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/Filter.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/Filter.cs index a32221e09..1dbbc8de5 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/Filter.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/Filter.cs @@ -9,4 +9,4 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp public const int SubpelShifts = 1 << SubpelBits; public const int SubpelTaps = 8; } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/IntraPred.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/IntraPred.cs index 8a570ed59..df72e160d 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/IntraPred.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/IntraPred.cs @@ -1,4 +1,4 @@ -using Ryujinx.Graphics.Nvdec.Vp9.Common; +using Ryujinx.Graphics.Nvdec.Vp9.Common; namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp { @@ -6,22 +6,22 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp { private static unsafe ref byte Dst(byte* dst, int stride, int x, int y) { - return ref dst[x + y * stride]; + return ref dst[x + (y * stride)]; } private static unsafe ref ushort Dst(ushort* dst, int stride, int x, int y) { - return ref dst[x + y * stride]; + return ref dst[x + (y * stride)]; } private static byte Avg3(byte a, byte b, byte c) { - return (byte)((a + 2 * b + c + 2) >> 2); + return (byte)((a + (2 * b) + c + 2) >> 2); } private static ushort Avg3(ushort a, ushort b, ushort c) { - return (ushort)((a + 2 * b + c + 2) >> 2); + return (ushort)((a + (2 * b) + c + 2) >> 2); } private static byte Avg2(byte a, byte b) @@ -34,26 +34,25 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp return (ushort)((a + b + 1) >> 1); } - public static unsafe void D207Predictor8x8(byte* dst, int stride, byte* above, byte* left) + public static unsafe void D207Predictor8X8(byte* dst, int stride, byte* above, byte* left) { D207Predictor(dst, stride, 8, above, left); } - public static unsafe void D207Predictor16x16(byte* dst, int stride, byte* above, byte* left) + public static unsafe void D207Predictor16X16(byte* dst, int stride, byte* above, byte* left) { D207Predictor(dst, stride, 16, above, left); } - public static unsafe void D207Predictor32x32(byte* dst, int stride, byte* above, byte* left) + public static unsafe void D207Predictor32X32(byte* dst, int stride, byte* above, byte* left) { D207Predictor(dst, stride, 32, above, left); } private static unsafe void D207Predictor(byte* dst, int stride, int bs, byte* above, byte* left) { - int r, c; // First column - for (r = 0; r < bs - 1; ++r) + for (int r = 0; r < bs - 1; ++r) { dst[r * stride] = Avg2(left[r], left[r + 1]); } @@ -62,7 +61,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp dst++; // Second column - for (r = 0; r < bs - 2; ++r) + for (int r = 0; r < bs - 2; ++r) { dst[r * stride] = Avg3(left[r], left[r + 1], left[r + 2]); } @@ -72,64 +71,63 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp dst++; // Rest of last row - for (c = 0; c < bs - 2; ++c) + for (int c = 0; c < bs - 2; ++c) { - dst[(bs - 1) * stride + c] = left[bs - 1]; + dst[((bs - 1) * stride) + c] = left[bs - 1]; } - for (r = bs - 2; r >= 0; --r) + for (int r = bs - 2; r >= 0; --r) { - for (c = 0; c < bs - 2; ++c) + for (int c = 0; c < bs - 2; ++c) { - dst[r * stride + c] = dst[(r + 1) * stride + c - 2]; + dst[(r * stride) + c] = dst[((r + 1) * stride) + c - 2]; } } } - public static unsafe void D63Predictor8x8(byte* dst, int stride, byte* above, byte* left) + public static unsafe void D63Predictor8X8(byte* dst, int stride, byte* above, byte* left) { D63Predictor(dst, stride, 8, above, left); } - public static unsafe void D63Predictor16x16(byte* dst, int stride, byte* above, byte* left) + public static unsafe void D63Predictor16X16(byte* dst, int stride, byte* above, byte* left) { D63Predictor(dst, stride, 16, above, left); } - public static unsafe void D63Predictor32x32(byte* dst, int stride, byte* above, byte* left) + public static unsafe void D63Predictor32X32(byte* dst, int stride, byte* above, byte* left) { D63Predictor(dst, stride, 32, above, left); } private static unsafe void D63Predictor(byte* dst, int stride, int bs, byte* above, byte* left) { - int r, c; - int size; - for (c = 0; c < bs; ++c) + for (int c = 0; c < bs; ++c) { dst[c] = Avg2(above[c], above[c + 1]); dst[stride + c] = Avg3(above[c], above[c + 1], above[c + 2]); } - for (r = 2, size = bs - 2; r < bs; r += 2, --size) + + for (int r = 2, size = bs - 2; r < bs; r += 2, --size) { - MemoryUtil.Copy(dst + (r + 0) * stride, dst + (r >> 1), size); - MemoryUtil.Fill(dst + (r + 0) * stride + size, above[bs - 1], bs - size); - MemoryUtil.Copy(dst + (r + 1) * stride, dst + stride + (r >> 1), size); - MemoryUtil.Fill(dst + (r + 1) * stride + size, above[bs - 1], bs - size); + MemoryUtil.Copy(dst + ((r + 0) * stride), dst + (r >> 1), size); + MemoryUtil.Fill(dst + ((r + 0) * stride) + size, above[bs - 1], bs - size); + MemoryUtil.Copy(dst + ((r + 1) * stride), dst + stride + (r >> 1), size); + MemoryUtil.Fill(dst + ((r + 1) * stride) + size, above[bs - 1], bs - size); } } - public static unsafe void D45Predictor8x8(byte* dst, int stride, byte* above, byte* left) + public static unsafe void D45Predictor8X8(byte* dst, int stride, byte* above, byte* left) { D45Predictor(dst, stride, 8, above, left); } - public static unsafe void D45Predictor16x16(byte* dst, int stride, byte* above, byte* left) + public static unsafe void D45Predictor16X16(byte* dst, int stride, byte* above, byte* left) { D45Predictor(dst, stride, 16, above, left); } - public static unsafe void D45Predictor32x32(byte* dst, int stride, byte* above, byte* left) + public static unsafe void D45Predictor32X32(byte* dst, int stride, byte* above, byte* left) { D45Predictor(dst, stride, 32, above, left); } @@ -138,15 +136,15 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp { byte aboveRight = above[bs - 1]; byte* dstRow0 = dst; - int x, size; - for (x = 0; x < bs - 1; ++x) + for (int x = 0; x < bs - 1; ++x) { dst[x] = Avg3(above[x], above[x + 1], above[x + 2]); } + dst[bs - 1] = aboveRight; dst += stride; - for (x = 1, size = bs - 2; x < bs; ++x, --size) + for (int x = 1, size = bs - 2; x < bs; ++x, --size) { MemoryUtil.Copy(dst, dstRow0 + x, size); MemoryUtil.Fill(dst + size, aboveRight, x + 1); @@ -154,27 +152,25 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp } } - public static unsafe void D117Predictor8x8(byte* dst, int stride, byte* above, byte* left) + public static unsafe void D117Predictor8X8(byte* dst, int stride, byte* above, byte* left) { D117Predictor(dst, stride, 8, above, left); } - public static unsafe void D117Predictor16x16(byte* dst, int stride, byte* above, byte* left) + public static unsafe void D117Predictor16X16(byte* dst, int stride, byte* above, byte* left) { D117Predictor(dst, stride, 16, above, left); } - public static unsafe void D117Predictor32x32(byte* dst, int stride, byte* above, byte* left) + public static unsafe void D117Predictor32X32(byte* dst, int stride, byte* above, byte* left) { D117Predictor(dst, stride, 32, above, left); } private static unsafe void D117Predictor(byte* dst, int stride, int bs, byte* above, byte* left) { - int r, c; - // First row - for (c = 0; c < bs; c++) + for (int c = 0; c < bs; c++) { dst[c] = Avg2(above[c - 1], above[c]); } @@ -183,7 +179,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp // Second row dst[0] = Avg3(left[0], above[-1], above[0]); - for (c = 1; c < bs; c++) + for (int c = 1; c < bs; c++) { dst[c] = Avg3(above[c - 2], above[c - 1], above[c]); } @@ -192,83 +188,82 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp // The rest of first col dst[0] = Avg3(above[-1], left[0], left[1]); - for (r = 3; r < bs; ++r) + for (int r = 3; r < bs; ++r) { dst[(r - 2) * stride] = Avg3(left[r - 3], left[r - 2], left[r - 1]); } // The rest of the block - for (r = 2; r < bs; ++r) + for (int r = 2; r < bs; ++r) { - for (c = 1; c < bs; c++) + for (int c = 1; c < bs; c++) { - dst[c] = dst[-2 * stride + c - 1]; + dst[c] = dst[(-2 * stride) + c - 1]; } dst += stride; } } - public static unsafe void D135Predictor8x8(byte* dst, int stride, byte* above, byte* left) + public static unsafe void D135Predictor8X8(byte* dst, int stride, byte* above, byte* left) { D135Predictor(dst, stride, 8, above, left); } - public static unsafe void D135Predictor16x16(byte* dst, int stride, byte* above, byte* left) + public static unsafe void D135Predictor16X16(byte* dst, int stride, byte* above, byte* left) { D135Predictor(dst, stride, 16, above, left); } - public static unsafe void D135Predictor32x32(byte* dst, int stride, byte* above, byte* left) + public static unsafe void D135Predictor32X32(byte* dst, int stride, byte* above, byte* left) { D135Predictor(dst, stride, 32, above, left); } private static unsafe void D135Predictor(byte* dst, int stride, int bs, byte* above, byte* left) { - int i; byte* border = stackalloc byte[32 + 32 - 1]; // outer border from bottom-left to top-right // Dst(dst, stride, bs, bs - 2)[0], i.e., border starting at bottom-left - for (i = 0; i < bs - 2; ++i) + for (int i = 0; i < bs - 2; ++i) { border[i] = Avg3(left[bs - 3 - i], left[bs - 2 - i], left[bs - 1 - i]); } + border[bs - 2] = Avg3(above[-1], left[0], left[1]); border[bs - 1] = Avg3(left[0], above[-1], above[0]); border[bs - 0] = Avg3(above[-1], above[0], above[1]); // dst[0][2, size), i.e., remaining top border ascending - for (i = 0; i < bs - 2; ++i) + for (int i = 0; i < bs - 2; ++i) { border[bs + 1 + i] = Avg3(above[i], above[i + 1], above[i + 2]); } - for (i = 0; i < bs; ++i) + for (int i = 0; i < bs; ++i) { - MemoryUtil.Copy(dst + i * stride, border + bs - 1 - i, bs); + MemoryUtil.Copy(dst + (i * stride), border + bs - 1 - i, bs); } } - public static unsafe void D153Predictor8x8(byte* dst, int stride, byte* above, byte* left) + public static unsafe void D153Predictor8X8(byte* dst, int stride, byte* above, byte* left) { D153Predictor(dst, stride, 8, above, left); } - public static unsafe void D153Predictor16x16(byte* dst, int stride, byte* above, byte* left) + public static unsafe void D153Predictor16X16(byte* dst, int stride, byte* above, byte* left) { D153Predictor(dst, stride, 16, above, left); } - public static unsafe void D153Predictor32x32(byte* dst, int stride, byte* above, byte* left) + public static unsafe void D153Predictor32X32(byte* dst, int stride, byte* above, byte* left) { D153Predictor(dst, stride, 32, above, left); } private static unsafe void D153Predictor(byte* dst, int stride, int bs, byte* above, byte* left) { - int r, c; dst[0] = Avg2(above[-1], left[0]); - for (r = 1; r < bs; r++) + for (int r = 1; r < bs; r++) { dst[r * stride] = Avg2(left[r - 1], left[r]); } @@ -277,23 +272,23 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp dst[0] = Avg3(left[0], above[-1], above[0]); dst[stride] = Avg3(above[-1], left[0], left[1]); - for (r = 2; r < bs; r++) + for (int r = 2; r < bs; r++) { dst[r * stride] = Avg3(left[r - 2], left[r - 1], left[r]); } dst++; - for (c = 0; c < bs - 2; c++) + for (int c = 0; c < bs - 2; c++) { dst[c] = Avg3(above[c - 1], above[c], above[c + 1]); } dst += stride; - for (r = 1; r < bs; ++r) + for (int r = 1; r < bs; ++r) { - for (c = 0; c < bs - 2; c++) + for (int c = 0; c < bs - 2; c++) { dst[c] = dst[-stride + c - 2]; } @@ -302,96 +297,91 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp } } - public static unsafe void VPredictor4x4(byte* dst, int stride, byte* above, byte* left) + public static unsafe void VPredictor4X4(byte* dst, int stride, byte* above, byte* left) { VPredictor(dst, stride, 4, above, left); } - public static unsafe void VPredictor8x8(byte* dst, int stride, byte* above, byte* left) + public static unsafe void VPredictor8X8(byte* dst, int stride, byte* above, byte* left) { VPredictor(dst, stride, 8, above, left); } - public static unsafe void VPredictor16x16(byte* dst, int stride, byte* above, byte* left) + public static unsafe void VPredictor16X16(byte* dst, int stride, byte* above, byte* left) { VPredictor(dst, stride, 16, above, left); } - public static unsafe void VPredictor32x32(byte* dst, int stride, byte* above, byte* left) + public static unsafe void VPredictor32X32(byte* dst, int stride, byte* above, byte* left) { VPredictor(dst, stride, 32, above, left); } private static unsafe void VPredictor(byte* dst, int stride, int bs, byte* above, byte* left) { - int r; - - for (r = 0; r < bs; r++) + for (int r = 0; r < bs; r++) { MemoryUtil.Copy(dst, above, bs); dst += stride; } } - public static unsafe void HPredictor4x4(byte* dst, int stride, byte* above, byte* left) + public static unsafe void HPredictor4X4(byte* dst, int stride, byte* above, byte* left) { HPredictor(dst, stride, 4, above, left); } - public static unsafe void HPredictor8x8(byte* dst, int stride, byte* above, byte* left) + public static unsafe void HPredictor8X8(byte* dst, int stride, byte* above, byte* left) { HPredictor(dst, stride, 8, above, left); } - public static unsafe void HPredictor16x16(byte* dst, int stride, byte* above, byte* left) + public static unsafe void HPredictor16X16(byte* dst, int stride, byte* above, byte* left) { HPredictor(dst, stride, 16, above, left); } - public static unsafe void HPredictor32x32(byte* dst, int stride, byte* above, byte* left) + public static unsafe void HPredictor32X32(byte* dst, int stride, byte* above, byte* left) { HPredictor(dst, stride, 32, above, left); } private static unsafe void HPredictor(byte* dst, int stride, int bs, byte* above, byte* left) { - int r; - - for (r = 0; r < bs; r++) + for (int r = 0; r < bs; r++) { MemoryUtil.Fill(dst, left[r], bs); dst += stride; } } - public static unsafe void TMPredictor4x4(byte* dst, int stride, byte* above, byte* left) + public static unsafe void TmPredictor4X4(byte* dst, int stride, byte* above, byte* left) { - TMPredictor(dst, stride, 4, above, left); + TmPredictor(dst, stride, 4, above, left); } - public static unsafe void TMPredictor8x8(byte* dst, int stride, byte* above, byte* left) + public static unsafe void TmPredictor8X8(byte* dst, int stride, byte* above, byte* left) { - TMPredictor(dst, stride, 8, above, left); + TmPredictor(dst, stride, 8, above, left); } - public static unsafe void TMPredictor16x16(byte* dst, int stride, byte* above, byte* left) + public static unsafe void TmPredictor16X16(byte* dst, int stride, byte* above, byte* left) { - TMPredictor(dst, stride, 16, above, left); + TmPredictor(dst, stride, 16, above, left); } - public static unsafe void TMPredictor32x32(byte* dst, int stride, byte* above, byte* left) + public static unsafe void TmPredictor32X32(byte* dst, int stride, byte* above, byte* left) { - TMPredictor(dst, stride, 32, above, left); + TmPredictor(dst, stride, 32, above, left); } - private static unsafe void TMPredictor(byte* dst, int stride, int bs, byte* above, byte* left) + private static unsafe void TmPredictor(byte* dst, int stride, int bs, byte* above, byte* left) { - int r, c; int yTopLeft = above[-1]; - for (r = 0; r < bs; r++) + for (int r = 0; r < bs; r++) { - for (c = 0; c < bs; c++) + for (int c = 0; c < bs; c++) { dst[c] = BitUtils.ClipPixel(left[r] + above[c] - yTopLeft); } @@ -400,201 +390,200 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp } } - public static unsafe void Dc128Predictor4x4(byte* dst, int stride, byte* above, byte* left) + public static unsafe void Dc128Predictor4X4(byte* dst, int stride, byte* above, byte* left) { Dc128Predictor(dst, stride, 4, above, left); } - public static unsafe void Dc128Predictor8x8(byte* dst, int stride, byte* above, byte* left) + public static unsafe void Dc128Predictor8X8(byte* dst, int stride, byte* above, byte* left) { Dc128Predictor(dst, stride, 8, above, left); } - public static unsafe void Dc128Predictor16x16(byte* dst, int stride, byte* above, byte* left) + public static unsafe void Dc128Predictor16X16(byte* dst, int stride, byte* above, byte* left) { Dc128Predictor(dst, stride, 16, above, left); } - public static unsafe void Dc128Predictor32x32(byte* dst, int stride, byte* above, byte* left) + public static unsafe void Dc128Predictor32X32(byte* dst, int stride, byte* above, byte* left) { Dc128Predictor(dst, stride, 32, above, left); } private static unsafe void Dc128Predictor(byte* dst, int stride, int bs, byte* above, byte* left) { - int r; - - for (r = 0; r < bs; r++) + for (int r = 0; r < bs; r++) { MemoryUtil.Fill(dst, (byte)128, bs); dst += stride; } } - public static unsafe void DcLeftPredictor4x4(byte* dst, int stride, byte* above, byte* left) + public static unsafe void DcLeftPredictor4X4(byte* dst, int stride, byte* above, byte* left) { DcLeftPredictor(dst, stride, 4, above, left); } - public static unsafe void DcLeftPredictor8x8(byte* dst, int stride, byte* above, byte* left) + public static unsafe void DcLeftPredictor8X8(byte* dst, int stride, byte* above, byte* left) { DcLeftPredictor(dst, stride, 8, above, left); } - public static unsafe void DcLeftPredictor16x16(byte* dst, int stride, byte* above, byte* left) + public static unsafe void DcLeftPredictor16X16(byte* dst, int stride, byte* above, byte* left) { DcLeftPredictor(dst, stride, 16, above, left); } - public static unsafe void DcLeftPredictor32x32(byte* dst, int stride, byte* above, byte* left) + public static unsafe void DcLeftPredictor32X32(byte* dst, int stride, byte* above, byte* left) { DcLeftPredictor(dst, stride, 32, above, left); } private static unsafe void DcLeftPredictor(byte* dst, int stride, int bs, byte* above, byte* left) { - int i, r, expectedDc, sum = 0; + int sum = 0; - for (i = 0; i < bs; i++) + for (int i = 0; i < bs; i++) { sum += left[i]; } - expectedDc = (sum + (bs >> 1)) / bs; + int expectedDc = (sum + (bs >> 1)) / bs; - for (r = 0; r < bs; r++) + for (int r = 0; r < bs; r++) { MemoryUtil.Fill(dst, (byte)expectedDc, bs); dst += stride; } } - public static unsafe void DcTopPredictor4x4(byte* dst, int stride, byte* above, byte* left) + public static unsafe void DcTopPredictor4X4(byte* dst, int stride, byte* above, byte* left) { DcTopPredictor(dst, stride, 4, above, left); } - public static unsafe void DcTopPredictor8x8(byte* dst, int stride, byte* above, byte* left) + public static unsafe void DcTopPredictor8X8(byte* dst, int stride, byte* above, byte* left) { DcTopPredictor(dst, stride, 8, above, left); } - public static unsafe void DcTopPredictor16x16(byte* dst, int stride, byte* above, byte* left) + public static unsafe void DcTopPredictor16X16(byte* dst, int stride, byte* above, byte* left) { DcTopPredictor(dst, stride, 16, above, left); } - public static unsafe void DcTopPredictor32x32(byte* dst, int stride, byte* above, byte* left) + public static unsafe void DcTopPredictor32X32(byte* dst, int stride, byte* above, byte* left) { DcTopPredictor(dst, stride, 32, above, left); } private static unsafe void DcTopPredictor(byte* dst, int stride, int bs, byte* above, byte* left) { - int i, r, expectedDc, sum = 0; + int sum = 0; - for (i = 0; i < bs; i++) + for (int i = 0; i < bs; i++) { sum += above[i]; } - expectedDc = (sum + (bs >> 1)) / bs; + int expectedDc = (sum + (bs >> 1)) / bs; - for (r = 0; r < bs; r++) + for (int r = 0; r < bs; r++) { MemoryUtil.Fill(dst, (byte)expectedDc, bs); dst += stride; } } - public static unsafe void DcPredictor4x4(byte* dst, int stride, byte* above, byte* left) + public static unsafe void DcPredictor4X4(byte* dst, int stride, byte* above, byte* left) { DcPredictor(dst, stride, 4, above, left); } - public static unsafe void DcPredictor8x8(byte* dst, int stride, byte* above, byte* left) + public static unsafe void DcPredictor8X8(byte* dst, int stride, byte* above, byte* left) { DcPredictor(dst, stride, 8, above, left); } - public static unsafe void DcPredictor16x16(byte* dst, int stride, byte* above, byte* left) + public static unsafe void DcPredictor16X16(byte* dst, int stride, byte* above, byte* left) { DcPredictor(dst, stride, 16, above, left); } - public static unsafe void DcPredictor32x32(byte* dst, int stride, byte* above, byte* left) + public static unsafe void DcPredictor32X32(byte* dst, int stride, byte* above, byte* left) { DcPredictor(dst, stride, 32, above, left); } private static unsafe void DcPredictor(byte* dst, int stride, int bs, byte* above, byte* left) { - int i, r, expectedDc, sum = 0; + int sum = 0; int count = 2 * bs; - for (i = 0; i < bs; i++) + for (int i = 0; i < bs; i++) { sum += above[i]; sum += left[i]; } - expectedDc = (sum + (count >> 1)) / count; + int expectedDc = (sum + (count >> 1)) / count; - for (r = 0; r < bs; r++) + for (int r = 0; r < bs; r++) { MemoryUtil.Fill(dst, (byte)expectedDc, bs); dst += stride; } } - public static unsafe void HePredictor4x4(byte* dst, int stride, byte* above, byte* left) + public static unsafe void HePredictor4X4(byte* dst, int stride, byte* above, byte* left) { byte h = above[-1]; - byte I = left[0]; + byte i = left[0]; byte j = left[1]; byte k = left[2]; byte l = left[3]; - MemoryUtil.Fill(dst + stride * 0, Avg3(h, I, j), 4); - MemoryUtil.Fill(dst + stride * 1, Avg3(I, j, k), 4); - MemoryUtil.Fill(dst + stride * 2, Avg3(j, k, l), 4); - MemoryUtil.Fill(dst + stride * 3, Avg3(k, l, l), 4); + MemoryUtil.Fill(dst + (stride * 0), Avg3(h, i, j), 4); + MemoryUtil.Fill(dst + (stride * 1), Avg3(i, j, k), 4); + MemoryUtil.Fill(dst + (stride * 2), Avg3(j, k, l), 4); + MemoryUtil.Fill(dst + (stride * 3), Avg3(k, l, l), 4); } - public static unsafe void VePredictor4x4(byte* dst, int stride, byte* above, byte* left) + public static unsafe void VePredictor4X4(byte* dst, int stride, byte* above, byte* left) { byte h = above[-1]; - byte I = above[0]; + byte i = above[0]; byte j = above[1]; byte k = above[2]; byte l = above[3]; byte m = above[4]; - dst[0] = Avg3(h, I, j); - dst[1] = Avg3(I, j, k); + dst[0] = Avg3(h, i, j); + dst[1] = Avg3(i, j, k); dst[2] = Avg3(j, k, l); dst[3] = Avg3(k, l, m); - MemoryUtil.Copy(dst + stride * 1, dst, 4); - MemoryUtil.Copy(dst + stride * 2, dst, 4); - MemoryUtil.Copy(dst + stride * 3, dst, 4); + MemoryUtil.Copy(dst + (stride * 1), dst, 4); + MemoryUtil.Copy(dst + (stride * 2), dst, 4); + MemoryUtil.Copy(dst + (stride * 3), dst, 4); } - public static unsafe void D207Predictor4x4(byte* dst, int stride, byte* above, byte* left) + public static unsafe void D207Predictor4X4(byte* dst, int stride, byte* above, byte* left) { - byte I = left[0]; + byte i = left[0]; byte j = left[1]; byte k = left[2]; byte l = left[3]; - Dst(dst, stride, 0, 0) = Avg2(I, j); + Dst(dst, stride, 0, 0) = Avg2(i, j); Dst(dst, stride, 2, 0) = Dst(dst, stride, 0, 1) = Avg2(j, k); Dst(dst, stride, 2, 1) = Dst(dst, stride, 0, 2) = Avg2(k, l); - Dst(dst, stride, 1, 0) = Avg3(I, j, k); + Dst(dst, stride, 1, 0) = Avg3(i, j, k); Dst(dst, stride, 3, 0) = Dst(dst, stride, 1, 1) = Avg3(j, k, l); Dst(dst, stride, 3, 1) = Dst(dst, stride, 1, 2) = Avg3(k, l, l); - Dst(dst, stride, 3, 2) = Dst(dst, stride, 2, 2) = Dst(dst, stride, 0, 3) = Dst(dst, stride, 1, 3) = Dst(dst, stride, 2, 3) = Dst(dst, stride, 3, 3) = l; + Dst(dst, stride, 3, 2) = Dst(dst, stride, 2, 2) = Dst(dst, stride, 0, 3) = + Dst(dst, stride, 1, 3) = Dst(dst, stride, 2, 3) = Dst(dst, stride, 3, 3) = l; } - public static unsafe void D63Predictor4x4(byte* dst, int stride, byte* above, byte* left) + public static unsafe void D63Predictor4X4(byte* dst, int stride, byte* above, byte* left) { byte a = above[0]; byte b = above[1]; @@ -616,7 +605,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp Dst(dst, stride, 3, 3) = Avg3(e, f, g); // Differs from vp8 } - public static unsafe void D63ePredictor4x4(byte* dst, int stride, byte* above, byte* left) + public static unsafe void D63EPredictor4X4(byte* dst, int stride, byte* above, byte* left) { byte a = above[0]; byte b = above[1]; @@ -639,7 +628,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp Dst(dst, stride, 3, 3) = Avg3(f, g, h); } - public static unsafe void D45Predictor4x4(byte* dst, int stride, byte* above, byte* left) + public static unsafe void D45Predictor4X4(byte* dst, int stride, byte* above, byte* left) { byte a = above[0]; byte b = above[1]; @@ -652,13 +641,14 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp Dst(dst, stride, 0, 0) = Avg3(a, b, c); Dst(dst, stride, 1, 0) = Dst(dst, stride, 0, 1) = Avg3(b, c, d); Dst(dst, stride, 2, 0) = Dst(dst, stride, 1, 1) = Dst(dst, stride, 0, 2) = Avg3(c, d, e); - Dst(dst, stride, 3, 0) = Dst(dst, stride, 2, 1) = Dst(dst, stride, 1, 2) = Dst(dst, stride, 0, 3) = Avg3(d, e, f); + Dst(dst, stride, 3, 0) = + Dst(dst, stride, 2, 1) = Dst(dst, stride, 1, 2) = Dst(dst, stride, 0, 3) = Avg3(d, e, f); Dst(dst, stride, 3, 1) = Dst(dst, stride, 2, 2) = Dst(dst, stride, 1, 3) = Avg3(e, f, g); Dst(dst, stride, 3, 2) = Dst(dst, stride, 2, 3) = Avg3(f, g, h); Dst(dst, stride, 3, 3) = h; // differs from vp8 } - public static unsafe void D45ePredictor4x4(byte* dst, int stride, byte* above, byte* left) + public static unsafe void D45EPredictor4X4(byte* dst, int stride, byte* above, byte* left) { byte a = above[0]; byte b = above[1]; @@ -671,15 +661,16 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp Dst(dst, stride, 0, 0) = Avg3(a, b, c); Dst(dst, stride, 1, 0) = Dst(dst, stride, 0, 1) = Avg3(b, c, d); Dst(dst, stride, 2, 0) = Dst(dst, stride, 1, 1) = Dst(dst, stride, 0, 2) = Avg3(c, d, e); - Dst(dst, stride, 3, 0) = Dst(dst, stride, 2, 1) = Dst(dst, stride, 1, 2) = Dst(dst, stride, 0, 3) = Avg3(d, e, f); + Dst(dst, stride, 3, 0) = + Dst(dst, stride, 2, 1) = Dst(dst, stride, 1, 2) = Dst(dst, stride, 0, 3) = Avg3(d, e, f); Dst(dst, stride, 3, 1) = Dst(dst, stride, 2, 2) = Dst(dst, stride, 1, 3) = Avg3(e, f, g); Dst(dst, stride, 3, 2) = Dst(dst, stride, 2, 3) = Avg3(f, g, h); Dst(dst, stride, 3, 3) = Avg3(g, h, h); } - public static unsafe void D117Predictor4x4(byte* dst, int stride, byte* above, byte* left) + public static unsafe void D117Predictor4X4(byte* dst, int stride, byte* above, byte* left) { - byte I = left[0]; + byte i = left[0]; byte j = left[1]; byte k = left[2]; byte x = above[-1]; @@ -692,17 +683,17 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp Dst(dst, stride, 2, 0) = Dst(dst, stride, 3, 2) = Avg2(b, c); Dst(dst, stride, 3, 0) = Avg2(c, d); - Dst(dst, stride, 0, 3) = Avg3(k, j, I); - Dst(dst, stride, 0, 2) = Avg3(j, I, x); - Dst(dst, stride, 0, 1) = Dst(dst, stride, 1, 3) = Avg3(I, x, a); + Dst(dst, stride, 0, 3) = Avg3(k, j, i); + Dst(dst, stride, 0, 2) = Avg3(j, i, x); + Dst(dst, stride, 0, 1) = Dst(dst, stride, 1, 3) = Avg3(i, x, a); Dst(dst, stride, 1, 1) = Dst(dst, stride, 2, 3) = Avg3(x, a, b); Dst(dst, stride, 2, 1) = Dst(dst, stride, 3, 3) = Avg3(a, b, c); Dst(dst, stride, 3, 1) = Avg3(b, c, d); } - public static unsafe void D135Predictor4x4(byte* dst, int stride, byte* above, byte* left) + public static unsafe void D135Predictor4X4(byte* dst, int stride, byte* above, byte* left) { - byte I = left[0]; + byte i = left[0]; byte j = left[1]; byte k = left[2]; byte l = left[3]; @@ -712,17 +703,18 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp byte c = above[2]; byte d = above[3]; Dst(dst, stride, 0, 3) = Avg3(j, k, l); - Dst(dst, stride, 1, 3) = Dst(dst, stride, 0, 2) = Avg3(I, j, k); - Dst(dst, stride, 2, 3) = Dst(dst, stride, 1, 2) = Dst(dst, stride, 0, 1) = Avg3(x, I, j); - Dst(dst, stride, 3, 3) = Dst(dst, stride, 2, 2) = Dst(dst, stride, 1, 1) = Dst(dst, stride, 0, 0) = Avg3(a, x, I); + Dst(dst, stride, 1, 3) = Dst(dst, stride, 0, 2) = Avg3(i, j, k); + Dst(dst, stride, 2, 3) = Dst(dst, stride, 1, 2) = Dst(dst, stride, 0, 1) = Avg3(x, i, j); + Dst(dst, stride, 3, 3) = + Dst(dst, stride, 2, 2) = Dst(dst, stride, 1, 1) = Dst(dst, stride, 0, 0) = Avg3(a, x, i); Dst(dst, stride, 3, 2) = Dst(dst, stride, 2, 1) = Dst(dst, stride, 1, 0) = Avg3(b, a, x); Dst(dst, stride, 3, 1) = Dst(dst, stride, 2, 0) = Avg3(c, b, a); Dst(dst, stride, 3, 0) = Avg3(d, c, b); } - public static unsafe void D153Predictor4x4(byte* dst, int stride, byte* above, byte* left) + public static unsafe void D153Predictor4X4(byte* dst, int stride, byte* above, byte* left) { - byte I = left[0]; + byte i = left[0]; byte j = left[1]; byte k = left[2]; byte l = left[3]; @@ -730,131 +722,133 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp byte a = above[0]; byte b = above[1]; byte c = above[2]; - Dst(dst, stride, 0, 0) = Dst(dst, stride, 2, 1) = Avg2(I, x); - Dst(dst, stride, 0, 1) = Dst(dst, stride, 2, 2) = Avg2(j, I); + Dst(dst, stride, 0, 0) = Dst(dst, stride, 2, 1) = Avg2(i, x); + Dst(dst, stride, 0, 1) = Dst(dst, stride, 2, 2) = Avg2(j, i); Dst(dst, stride, 0, 2) = Dst(dst, stride, 2, 3) = Avg2(k, j); Dst(dst, stride, 0, 3) = Avg2(l, k); Dst(dst, stride, 3, 0) = Avg3(a, b, c); Dst(dst, stride, 2, 0) = Avg3(x, a, b); - Dst(dst, stride, 1, 0) = Dst(dst, stride, 3, 1) = Avg3(I, x, a); - Dst(dst, stride, 1, 1) = Dst(dst, stride, 3, 2) = Avg3(j, I, x); - Dst(dst, stride, 1, 2) = Dst(dst, stride, 3, 3) = Avg3(k, j, I); + Dst(dst, stride, 1, 0) = Dst(dst, stride, 3, 1) = Avg3(i, x, a); + Dst(dst, stride, 1, 1) = Dst(dst, stride, 3, 2) = Avg3(j, i, x); + Dst(dst, stride, 1, 2) = Dst(dst, stride, 3, 3) = Avg3(k, j, i); Dst(dst, stride, 1, 3) = Avg3(l, k, j); } - public static unsafe void HighbdD207Predictor8x8(ushort* dst, int stride, ushort* above, ushort* left, int bd) + public static unsafe void HighbdD207Predictor8X8(ushort* dst, int stride, ushort* above, ushort* left, int bd) { HighbdD207Predictor(dst, stride, 8, above, left, bd); } - public static unsafe void HighbdD207Predictor16x16(ushort* dst, int stride, ushort* above, ushort* left, int bd) + public static unsafe void HighbdD207Predictor16X16(ushort* dst, int stride, ushort* above, ushort* left, int bd) { HighbdD207Predictor(dst, stride, 16, above, left, bd); } - public static unsafe void HighbdD207Predictor32x32(ushort* dst, int stride, ushort* above, ushort* left, int bd) + public static unsafe void HighbdD207Predictor32X32(ushort* dst, int stride, ushort* above, ushort* left, int bd) { HighbdD207Predictor(dst, stride, 32, above, left, bd); } - private static unsafe void HighbdD207Predictor(ushort* dst, int stride, int bs, ushort* above, ushort* left, int bd) + private static unsafe void HighbdD207Predictor(ushort* dst, int stride, int bs, ushort* above, ushort* left, + int bd) { - int r, c; - // First column. - for (r = 0; r < bs - 1; ++r) + for (int r = 0; r < bs - 1; ++r) { dst[r * stride] = Avg2(left[r], left[r + 1]); } + dst[(bs - 1) * stride] = left[bs - 1]; dst++; // Second column. - for (r = 0; r < bs - 2; ++r) + for (int r = 0; r < bs - 2; ++r) { dst[r * stride] = Avg3(left[r], left[r + 1], left[r + 2]); } + dst[(bs - 2) * stride] = Avg3(left[bs - 2], left[bs - 1], left[bs - 1]); dst[(bs - 1) * stride] = left[bs - 1]; dst++; // Rest of last row. - for (c = 0; c < bs - 2; ++c) + for (int c = 0; c < bs - 2; ++c) { - dst[(bs - 1) * stride + c] = left[bs - 1]; + dst[((bs - 1) * stride) + c] = left[bs - 1]; } - for (r = bs - 2; r >= 0; --r) + for (int r = bs - 2; r >= 0; --r) { - for (c = 0; c < bs - 2; ++c) + for (int c = 0; c < bs - 2; ++c) { - dst[r * stride + c] = dst[(r + 1) * stride + c - 2]; + dst[(r * stride) + c] = dst[((r + 1) * stride) + c - 2]; } } } - public static unsafe void HighbdD63Predictor8x8(ushort* dst, int stride, ushort* above, ushort* left, int bd) + public static unsafe void HighbdD63Predictor8X8(ushort* dst, int stride, ushort* above, ushort* left, int bd) { HighbdD63Predictor(dst, stride, 8, above, left, bd); } - public static unsafe void HighbdD63Predictor16x16(ushort* dst, int stride, ushort* above, ushort* left, int bd) + public static unsafe void HighbdD63Predictor16X16(ushort* dst, int stride, ushort* above, ushort* left, int bd) { HighbdD63Predictor(dst, stride, 16, above, left, bd); } - public static unsafe void HighbdD63Predictor32x32(ushort* dst, int stride, ushort* above, ushort* left, int bd) + public static unsafe void HighbdD63Predictor32X32(ushort* dst, int stride, ushort* above, ushort* left, int bd) { HighbdD63Predictor(dst, stride, 32, above, left, bd); } - private static unsafe void HighbdD63Predictor(ushort* dst, int stride, int bs, ushort* above, ushort* left, int bd) + private static unsafe void HighbdD63Predictor(ushort* dst, int stride, int bs, ushort* above, ushort* left, + int bd) { - int r, c; - int size; - for (c = 0; c < bs; ++c) + for (int c = 0; c < bs; ++c) { dst[c] = Avg2(above[c], above[c + 1]); dst[stride + c] = Avg3(above[c], above[c + 1], above[c + 2]); } - for (r = 2, size = bs - 2; r < bs; r += 2, --size) + + for (int r = 2, size = bs - 2; r < bs; r += 2, --size) { - MemoryUtil.Copy(dst + (r + 0) * stride, dst + (r >> 1), size); - MemoryUtil.Fill(dst + (r + 0) * stride + size, above[bs - 1], bs - size); - MemoryUtil.Copy(dst + (r + 1) * stride, dst + stride + (r >> 1), size); - MemoryUtil.Fill(dst + (r + 1) * stride + size, above[bs - 1], bs - size); + MemoryUtil.Copy(dst + ((r + 0) * stride), dst + (r >> 1), size); + MemoryUtil.Fill(dst + ((r + 0) * stride) + size, above[bs - 1], bs - size); + MemoryUtil.Copy(dst + ((r + 1) * stride), dst + stride + (r >> 1), size); + MemoryUtil.Fill(dst + ((r + 1) * stride) + size, above[bs - 1], bs - size); } } - public static unsafe void HighbdD45Predictor8x8(ushort* dst, int stride, ushort* above, ushort* left, int bd) + public static unsafe void HighbdD45Predictor8X8(ushort* dst, int stride, ushort* above, ushort* left, int bd) { HighbdD45Predictor(dst, stride, 8, above, left, bd); } - public static unsafe void HighbdD45Predictor16x16(ushort* dst, int stride, ushort* above, ushort* left, int bd) + public static unsafe void HighbdD45Predictor16X16(ushort* dst, int stride, ushort* above, ushort* left, int bd) { HighbdD45Predictor(dst, stride, 16, above, left, bd); } - public static unsafe void HighbdD45Predictor32x32(ushort* dst, int stride, ushort* above, ushort* left, int bd) + public static unsafe void HighbdD45Predictor32X32(ushort* dst, int stride, ushort* above, ushort* left, int bd) { HighbdD45Predictor(dst, stride, 32, above, left, bd); } - private static unsafe void HighbdD45Predictor(ushort* dst, int stride, int bs, ushort* above, ushort* left, int bd) + private static unsafe void HighbdD45Predictor(ushort* dst, int stride, int bs, ushort* above, ushort* left, + int bd) { ushort aboveRight = above[bs - 1]; ushort* dstRow0 = dst; - int x, size; - for (x = 0; x < bs - 1; ++x) + for (int x = 0; x < bs - 1; ++x) { dst[x] = Avg3(above[x], above[x + 1], above[x + 2]); } + dst[bs - 1] = aboveRight; dst += stride; - for (x = 1, size = bs - 2; x < bs; ++x, --size) + for (int x = 1, size = bs - 2; x < bs; ++x, --size) { MemoryUtil.Copy(dst, dstRow0 + x, size); MemoryUtil.Fill(dst + size, aboveRight, x + 1); @@ -862,27 +856,26 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp } } - public static unsafe void HighbdD117Predictor8x8(ushort* dst, int stride, ushort* above, ushort* left, int bd) + public static unsafe void HighbdD117Predictor8X8(ushort* dst, int stride, ushort* above, ushort* left, int bd) { HighbdD117Predictor(dst, stride, 8, above, left, bd); } - public static unsafe void HighbdD117Predictor16x16(ushort* dst, int stride, ushort* above, ushort* left, int bd) + public static unsafe void HighbdD117Predictor16X16(ushort* dst, int stride, ushort* above, ushort* left, int bd) { HighbdD117Predictor(dst, stride, 16, above, left, bd); } - public static unsafe void HighbdD117Predictor32x32(ushort* dst, int stride, ushort* above, ushort* left, int bd) + public static unsafe void HighbdD117Predictor32X32(ushort* dst, int stride, ushort* above, ushort* left, int bd) { HighbdD117Predictor(dst, stride, 32, above, left, bd); } - private static unsafe void HighbdD117Predictor(ushort* dst, int stride, int bs, ushort* above, ushort* left, int bd) + private static unsafe void HighbdD117Predictor(ushort* dst, int stride, int bs, ushort* above, ushort* left, + int bd) { - int r, c; - // First row - for (c = 0; c < bs; c++) + for (int c = 0; c < bs; c++) { dst[c] = Avg2(above[c - 1], above[c]); } @@ -891,7 +884,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp // Second row dst[0] = Avg3(left[0], above[-1], above[0]); - for (c = 1; c < bs; c++) + for (int c = 1; c < bs; c++) { dst[c] = Avg3(above[c - 2], above[c - 1], above[c]); } @@ -900,83 +893,84 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp // The rest of first col dst[0] = Avg3(above[-1], left[0], left[1]); - for (r = 3; r < bs; ++r) + for (int r = 3; r < bs; ++r) { dst[(r - 2) * stride] = Avg3(left[r - 3], left[r - 2], left[r - 1]); } // The rest of the block - for (r = 2; r < bs; ++r) + for (int r = 2; r < bs; ++r) { - for (c = 1; c < bs; c++) + for (int c = 1; c < bs; c++) { - dst[c] = dst[-2 * stride + c - 1]; + dst[c] = dst[(-2 * stride) + c - 1]; } dst += stride; } } - public static unsafe void HighbdD135Predictor8x8(ushort* dst, int stride, ushort* above, ushort* left, int bd) + public static unsafe void HighbdD135Predictor8X8(ushort* dst, int stride, ushort* above, ushort* left, int bd) { HighbdD135Predictor(dst, stride, 8, above, left, bd); } - public static unsafe void HighbdD135Predictor16x16(ushort* dst, int stride, ushort* above, ushort* left, int bd) + public static unsafe void HighbdD135Predictor16X16(ushort* dst, int stride, ushort* above, ushort* left, int bd) { HighbdD135Predictor(dst, stride, 16, above, left, bd); } - public static unsafe void HighbdD135Predictor32x32(ushort* dst, int stride, ushort* above, ushort* left, int bd) + public static unsafe void HighbdD135Predictor32X32(ushort* dst, int stride, ushort* above, ushort* left, int bd) { HighbdD135Predictor(dst, stride, 32, above, left, bd); } - private static unsafe void HighbdD135Predictor(ushort* dst, int stride, int bs, ushort* above, ushort* left, int bd) + private static unsafe void HighbdD135Predictor(ushort* dst, int stride, int bs, ushort* above, ushort* left, + int bd) { - int i; ushort* border = stackalloc ushort[32 + 32 - 1]; // Outer border from bottom-left to top-right // Dst(dst, stride, bs, bs - 2)[0], i.e., border starting at bottom-left - for (i = 0; i < bs - 2; ++i) + for (int i = 0; i < bs - 2; ++i) { border[i] = Avg3(left[bs - 3 - i], left[bs - 2 - i], left[bs - 1 - i]); } + border[bs - 2] = Avg3(above[-1], left[0], left[1]); border[bs - 1] = Avg3(left[0], above[-1], above[0]); border[bs - 0] = Avg3(above[-1], above[0], above[1]); // dst[0][2, size), i.e., remaining top border ascending - for (i = 0; i < bs - 2; ++i) + for (int i = 0; i < bs - 2; ++i) { border[bs + 1 + i] = Avg3(above[i], above[i + 1], above[i + 2]); } - for (i = 0; i < bs; ++i) + for (int i = 0; i < bs; ++i) { - MemoryUtil.Copy(dst + i * stride, border + bs - 1 - i, bs); + MemoryUtil.Copy(dst + (i * stride), border + bs - 1 - i, bs); } } - public static unsafe void HighbdD153Predictor8x8(ushort* dst, int stride, ushort* above, ushort* left, int bd) + public static unsafe void HighbdD153Predictor8X8(ushort* dst, int stride, ushort* above, ushort* left, int bd) { HighbdD153Predictor(dst, stride, 8, above, left, bd); } - public static unsafe void HighbdD153Predictor16x16(ushort* dst, int stride, ushort* above, ushort* left, int bd) + public static unsafe void HighbdD153Predictor16X16(ushort* dst, int stride, ushort* above, ushort* left, int bd) { HighbdD153Predictor(dst, stride, 16, above, left, bd); } - public static unsafe void HighbdD153Predictor32x32(ushort* dst, int stride, ushort* above, ushort* left, int bd) + public static unsafe void HighbdD153Predictor32X32(ushort* dst, int stride, ushort* above, ushort* left, int bd) { HighbdD153Predictor(dst, stride, 32, above, left, bd); } - private static unsafe void HighbdD153Predictor(ushort* dst, int stride, int bs, ushort* above, ushort* left, int bd) + private static unsafe void HighbdD153Predictor(ushort* dst, int stride, int bs, ushort* above, ushort* left, + int bd) { - int r, c; dst[0] = Avg2(above[-1], left[0]); - for (r = 1; r < bs; r++) + for (int r = 1; r < bs; r++) { dst[r * stride] = Avg2(left[r - 1], left[r]); } @@ -985,23 +979,23 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp dst[0] = Avg3(left[0], above[-1], above[0]); dst[stride] = Avg3(above[-1], left[0], left[1]); - for (r = 2; r < bs; r++) + for (int r = 2; r < bs; r++) { dst[r * stride] = Avg3(left[r - 2], left[r - 1], left[r]); } dst++; - for (c = 0; c < bs - 2; c++) + for (int c = 0; c < bs - 2; c++) { dst[c] = Avg3(above[c - 1], above[c], above[c + 1]); } dst += stride; - for (r = 1; r < bs; ++r) + for (int r = 1; r < bs; ++r) { - for (c = 0; c < bs - 2; c++) + for (int c = 0; c < bs - 2; c++) { dst[c] = dst[-stride + c - 2]; } @@ -1010,94 +1004,94 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp } } - public static unsafe void HighbdVPredictor4x4(ushort* dst, int stride, ushort* above, ushort* left, int bd) + public static unsafe void HighbdVPredictor4X4(ushort* dst, int stride, ushort* above, ushort* left, int bd) { HighbdVPredictor(dst, stride, 4, above, left, bd); } - public static unsafe void HighbdVPredictor8x8(ushort* dst, int stride, ushort* above, ushort* left, int bd) + public static unsafe void HighbdVPredictor8X8(ushort* dst, int stride, ushort* above, ushort* left, int bd) { HighbdVPredictor(dst, stride, 8, above, left, bd); } - public static unsafe void HighbdVPredictor16x16(ushort* dst, int stride, ushort* above, ushort* left, int bd) + public static unsafe void HighbdVPredictor16X16(ushort* dst, int stride, ushort* above, ushort* left, int bd) { HighbdVPredictor(dst, stride, 16, above, left, bd); } - public static unsafe void HighbdVPredictor32x32(ushort* dst, int stride, ushort* above, ushort* left, int bd) + public static unsafe void HighbdVPredictor32X32(ushort* dst, int stride, ushort* above, ushort* left, int bd) { HighbdVPredictor(dst, stride, 32, above, left, bd); } - private static unsafe void HighbdVPredictor(ushort* dst, int stride, int bs, ushort* above, ushort* left, int bd) + private static unsafe void HighbdVPredictor(ushort* dst, int stride, int bs, ushort* above, ushort* left, + int bd) { - int r; - for (r = 0; r < bs; r++) + for (int r = 0; r < bs; r++) { MemoryUtil.Copy(dst, above, bs); dst += stride; } } - public static unsafe void HighbdHPredictor4x4(ushort* dst, int stride, ushort* above, ushort* left, int bd) + public static unsafe void HighbdHPredictor4X4(ushort* dst, int stride, ushort* above, ushort* left, int bd) { HighbdHPredictor(dst, stride, 4, above, left, bd); } - public static unsafe void HighbdHPredictor8x8(ushort* dst, int stride, ushort* above, ushort* left, int bd) + public static unsafe void HighbdHPredictor8X8(ushort* dst, int stride, ushort* above, ushort* left, int bd) { HighbdHPredictor(dst, stride, 8, above, left, bd); } - public static unsafe void HighbdHPredictor16x16(ushort* dst, int stride, ushort* above, ushort* left, int bd) + public static unsafe void HighbdHPredictor16X16(ushort* dst, int stride, ushort* above, ushort* left, int bd) { HighbdHPredictor(dst, stride, 16, above, left, bd); } - public static unsafe void HighbdHPredictor32x32(ushort* dst, int stride, ushort* above, ushort* left, int bd) + public static unsafe void HighbdHPredictor32X32(ushort* dst, int stride, ushort* above, ushort* left, int bd) { HighbdHPredictor(dst, stride, 32, above, left, bd); } - private static unsafe void HighbdHPredictor(ushort* dst, int stride, int bs, ushort* above, ushort* left, int bd) + private static unsafe void HighbdHPredictor(ushort* dst, int stride, int bs, ushort* above, ushort* left, + int bd) { - int r; - for (r = 0; r < bs; r++) + for (int r = 0; r < bs; r++) { MemoryUtil.Fill(dst, left[r], bs); dst += stride; } } - public static unsafe void HighbdTMPredictor4x4(ushort* dst, int stride, ushort* above, ushort* left, int bd) + public static unsafe void HighbdTmPredictor4X4(ushort* dst, int stride, ushort* above, ushort* left, int bd) { - HighbdTMPredictor(dst, stride, 4, above, left, bd); + HighbdTmPredictor(dst, stride, 4, above, left, bd); } - public static unsafe void HighbdTMPredictor8x8(ushort* dst, int stride, ushort* above, ushort* left, int bd) + public static unsafe void HighbdTmPredictor8X8(ushort* dst, int stride, ushort* above, ushort* left, int bd) { - HighbdTMPredictor(dst, stride, 8, above, left, bd); + HighbdTmPredictor(dst, stride, 8, above, left, bd); } - public static unsafe void HighbdTMPredictor16x16(ushort* dst, int stride, ushort* above, ushort* left, int bd) + public static unsafe void HighbdTmPredictor16X16(ushort* dst, int stride, ushort* above, ushort* left, int bd) { - HighbdTMPredictor(dst, stride, 16, above, left, bd); + HighbdTmPredictor(dst, stride, 16, above, left, bd); } - public static unsafe void HighbdTMPredictor32x32(ushort* dst, int stride, ushort* above, ushort* left, int bd) + public static unsafe void HighbdTmPredictor32X32(ushort* dst, int stride, ushort* above, ushort* left, int bd) { - HighbdTMPredictor(dst, stride, 32, above, left, bd); + HighbdTmPredictor(dst, stride, 32, above, left, bd); } - private static unsafe void HighbdTMPredictor(ushort* dst, int stride, int bs, ushort* above, ushort* left, int bd) + private static unsafe void HighbdTmPredictor(ushort* dst, int stride, int bs, ushort* above, ushort* left, + int bd) { - int r, c; int yTopLeft = above[-1]; - for (r = 0; r < bs; r++) + for (int r = 0; r < bs; r++) { - for (c = 0; c < bs; c++) + for (int c = 0; c < bs; c++) { dst[c] = BitUtils.ClipPixelHighbd(left[r] + above[c] - yTopLeft, bd); } @@ -1106,169 +1100,178 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp } } - public static unsafe void HighbdDc128Predictor4x4(ushort* dst, int stride, ushort* above, ushort* left, int bd) + public static unsafe void HighbdDc128Predictor4X4(ushort* dst, int stride, ushort* above, ushort* left, int bd) { HighbdDc128Predictor(dst, stride, 4, above, left, bd); } - public static unsafe void HighbdDc128Predictor8x8(ushort* dst, int stride, ushort* above, ushort* left, int bd) + public static unsafe void HighbdDc128Predictor8X8(ushort* dst, int stride, ushort* above, ushort* left, int bd) { HighbdDc128Predictor(dst, stride, 8, above, left, bd); } - public static unsafe void HighbdDc128Predictor16x16(ushort* dst, int stride, ushort* above, ushort* left, int bd) + public static unsafe void HighbdDc128Predictor16X16(ushort* dst, int stride, ushort* above, ushort* left, + int bd) { HighbdDc128Predictor(dst, stride, 16, above, left, bd); } - public static unsafe void HighbdDc128Predictor32x32(ushort* dst, int stride, ushort* above, ushort* left, int bd) + public static unsafe void HighbdDc128Predictor32X32(ushort* dst, int stride, ushort* above, ushort* left, + int bd) { HighbdDc128Predictor(dst, stride, 32, above, left, bd); } - private static unsafe void HighbdDc128Predictor(ushort* dst, int stride, int bs, ushort* above, ushort* left, int bd) + private static unsafe void HighbdDc128Predictor(ushort* dst, int stride, int bs, ushort* above, ushort* left, + int bd) { - int r; - - for (r = 0; r < bs; r++) + for (int r = 0; r < bs; r++) { MemoryUtil.Fill(dst, (ushort)(128 << (bd - 8)), bs); dst += stride; } } - public static unsafe void HighbdDcLeftPredictor4x4(ushort* dst, int stride, ushort* above, ushort* left, int bd) + public static unsafe void HighbdDcLeftPredictor4X4(ushort* dst, int stride, ushort* above, ushort* left, int bd) { HighbdDcLeftPredictor(dst, stride, 4, above, left, bd); } - public static unsafe void HighbdDcLeftPredictor8x8(ushort* dst, int stride, ushort* above, ushort* left, int bd) + public static unsafe void HighbdDcLeftPredictor8X8(ushort* dst, int stride, ushort* above, ushort* left, int bd) { HighbdDcLeftPredictor(dst, stride, 8, above, left, bd); } - public static unsafe void HighbdDcLeftPredictor16x16(ushort* dst, int stride, ushort* above, ushort* left, int bd) + public static unsafe void HighbdDcLeftPredictor16X16(ushort* dst, int stride, ushort* above, ushort* left, + int bd) { HighbdDcLeftPredictor(dst, stride, 16, above, left, bd); } - public static unsafe void HighbdDcLeftPredictor32x32(ushort* dst, int stride, ushort* above, ushort* left, int bd) + public static unsafe void HighbdDcLeftPredictor32X32(ushort* dst, int stride, ushort* above, ushort* left, + int bd) { HighbdDcLeftPredictor(dst, stride, 32, above, left, bd); } - private static unsafe void HighbdDcLeftPredictor(ushort* dst, int stride, int bs, ushort* above, ushort* left, int bd) + private static unsafe void HighbdDcLeftPredictor(ushort* dst, int stride, int bs, ushort* above, ushort* left, + int bd) { - int i, r, expectedDc, sum = 0; + int sum = 0; - for (i = 0; i < bs; i++) + for (int i = 0; i < bs; i++) { sum += left[i]; } - expectedDc = (sum + (bs >> 1)) / bs; + int expectedDc = (sum + (bs >> 1)) / bs; - for (r = 0; r < bs; r++) + for (int r = 0; r < bs; r++) { MemoryUtil.Fill(dst, (ushort)expectedDc, bs); dst += stride; } } - public static unsafe void HighbdDcTopPredictor4x4(ushort* dst, int stride, ushort* above, ushort* left, int bd) + public static unsafe void HighbdDcTopPredictor4X4(ushort* dst, int stride, ushort* above, ushort* left, int bd) { HighbdDcTopPredictor(dst, stride, 4, above, left, bd); } - public static unsafe void HighbdDcTopPredictor8x8(ushort* dst, int stride, ushort* above, ushort* left, int bd) + public static unsafe void HighbdDcTopPredictor8X8(ushort* dst, int stride, ushort* above, ushort* left, int bd) { HighbdDcTopPredictor(dst, stride, 8, above, left, bd); } - public static unsafe void HighbdDcTopPredictor16x16(ushort* dst, int stride, ushort* above, ushort* left, int bd) + public static unsafe void HighbdDcTopPredictor16X16(ushort* dst, int stride, ushort* above, ushort* left, + int bd) { HighbdDcTopPredictor(dst, stride, 16, above, left, bd); } - public static unsafe void HighbdDcTopPredictor32x32(ushort* dst, int stride, ushort* above, ushort* left, int bd) + public static unsafe void HighbdDcTopPredictor32X32(ushort* dst, int stride, ushort* above, ushort* left, + int bd) { HighbdDcTopPredictor(dst, stride, 32, above, left, bd); } - private static unsafe void HighbdDcTopPredictor(ushort* dst, int stride, int bs, ushort* above, ushort* left, int bd) + private static unsafe void HighbdDcTopPredictor(ushort* dst, int stride, int bs, ushort* above, ushort* left, + int bd) { - int i, r, expectedDc, sum = 0; + int sum = 0; - for (i = 0; i < bs; i++) + for (int i = 0; i < bs; i++) { sum += above[i]; } - expectedDc = (sum + (bs >> 1)) / bs; + int expectedDc = (sum + (bs >> 1)) / bs; - for (r = 0; r < bs; r++) + for (int r = 0; r < bs; r++) { MemoryUtil.Fill(dst, (ushort)expectedDc, bs); dst += stride; } } - public static unsafe void HighbdDcPredictor4x4(ushort* dst, int stride, ushort* above, ushort* left, int bd) + public static unsafe void HighbdDcPredictor4X4(ushort* dst, int stride, ushort* above, ushort* left, int bd) { HighbdDcPredictor(dst, stride, 4, above, left, bd); } - public static unsafe void HighbdDcPredictor8x8(ushort* dst, int stride, ushort* above, ushort* left, int bd) + public static unsafe void HighbdDcPredictor8X8(ushort* dst, int stride, ushort* above, ushort* left, int bd) { HighbdDcPredictor(dst, stride, 8, above, left, bd); } - public static unsafe void HighbdDcPredictor16x16(ushort* dst, int stride, ushort* above, ushort* left, int bd) + public static unsafe void HighbdDcPredictor16X16(ushort* dst, int stride, ushort* above, ushort* left, int bd) { HighbdDcPredictor(dst, stride, 16, above, left, bd); } - public static unsafe void HighbdDcPredictor32x32(ushort* dst, int stride, ushort* above, ushort* left, int bd) + public static unsafe void HighbdDcPredictor32X32(ushort* dst, int stride, ushort* above, ushort* left, int bd) { HighbdDcPredictor(dst, stride, 32, above, left, bd); } - private static unsafe void HighbdDcPredictor(ushort* dst, int stride, int bs, ushort* above, ushort* left, int bd) + private static unsafe void HighbdDcPredictor(ushort* dst, int stride, int bs, ushort* above, ushort* left, + int bd) { - int i, r, expectedDc, sum = 0; + int sum = 0; int count = 2 * bs; - for (i = 0; i < bs; i++) + for (int i = 0; i < bs; i++) { sum += above[i]; sum += left[i]; } - expectedDc = (sum + (count >> 1)) / count; + int expectedDc = (sum + (count >> 1)) / count; - for (r = 0; r < bs; r++) + for (int r = 0; r < bs; r++) { MemoryUtil.Fill(dst, (ushort)expectedDc, bs); dst += stride; } } - public static unsafe void HighbdD207Predictor4x4(ushort* dst, int stride, ushort* above, ushort* left, int bd) + public static unsafe void HighbdD207Predictor4X4(ushort* dst, int stride, ushort* above, ushort* left, int bd) { - ushort I = left[0]; + ushort i = left[0]; ushort j = left[1]; ushort k = left[2]; ushort l = left[3]; - Dst(dst, stride, 0, 0) = Avg2(I, j); + Dst(dst, stride, 0, 0) = Avg2(i, j); Dst(dst, stride, 2, 0) = Dst(dst, stride, 0, 1) = Avg2(j, k); Dst(dst, stride, 2, 1) = Dst(dst, stride, 0, 2) = Avg2(k, l); - Dst(dst, stride, 1, 0) = Avg3(I, j, k); + Dst(dst, stride, 1, 0) = Avg3(i, j, k); Dst(dst, stride, 3, 0) = Dst(dst, stride, 1, 1) = Avg3(j, k, l); Dst(dst, stride, 3, 1) = Dst(dst, stride, 1, 2) = Avg3(k, l, l); - Dst(dst, stride, 3, 2) = Dst(dst, stride, 2, 2) = Dst(dst, stride, 0, 3) = Dst(dst, stride, 1, 3) = Dst(dst, stride, 2, 3) = Dst(dst, stride, 3, 3) = l; + Dst(dst, stride, 3, 2) = Dst(dst, stride, 2, 2) = Dst(dst, stride, 0, 3) = + Dst(dst, stride, 1, 3) = Dst(dst, stride, 2, 3) = Dst(dst, stride, 3, 3) = l; } - public static unsafe void HighbdD63Predictor4x4(ushort* dst, int stride, ushort* above, ushort* left, int bd) + public static unsafe void HighbdD63Predictor4X4(ushort* dst, int stride, ushort* above, ushort* left, int bd) { ushort a = above[0]; ushort b = above[1]; @@ -1290,7 +1293,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp Dst(dst, stride, 3, 3) = Avg3(e, f, g); // Differs from vp8 } - public static unsafe void HighbdD45Predictor4x4(ushort* dst, int stride, ushort* above, ushort* left, int bd) + public static unsafe void HighbdD45Predictor4X4(ushort* dst, int stride, ushort* above, ushort* left, int bd) { ushort a = above[0]; ushort b = above[1]; @@ -1303,15 +1306,16 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp Dst(dst, stride, 0, 0) = Avg3(a, b, c); Dst(dst, stride, 1, 0) = Dst(dst, stride, 0, 1) = Avg3(b, c, d); Dst(dst, stride, 2, 0) = Dst(dst, stride, 1, 1) = Dst(dst, stride, 0, 2) = Avg3(c, d, e); - Dst(dst, stride, 3, 0) = Dst(dst, stride, 2, 1) = Dst(dst, stride, 1, 2) = Dst(dst, stride, 0, 3) = Avg3(d, e, f); + Dst(dst, stride, 3, 0) = + Dst(dst, stride, 2, 1) = Dst(dst, stride, 1, 2) = Dst(dst, stride, 0, 3) = Avg3(d, e, f); Dst(dst, stride, 3, 1) = Dst(dst, stride, 2, 2) = Dst(dst, stride, 1, 3) = Avg3(e, f, g); Dst(dst, stride, 3, 2) = Dst(dst, stride, 2, 3) = Avg3(f, g, h); Dst(dst, stride, 3, 3) = h; // Differs from vp8 } - public static unsafe void HighbdD117Predictor4x4(ushort* dst, int stride, ushort* above, ushort* left, int bd) + public static unsafe void HighbdD117Predictor4X4(ushort* dst, int stride, ushort* above, ushort* left, int bd) { - ushort I = left[0]; + ushort i = left[0]; ushort j = left[1]; ushort k = left[2]; ushort x = above[-1]; @@ -1324,17 +1328,17 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp Dst(dst, stride, 2, 0) = Dst(dst, stride, 3, 2) = Avg2(b, c); Dst(dst, stride, 3, 0) = Avg2(c, d); - Dst(dst, stride, 0, 3) = Avg3(k, j, I); - Dst(dst, stride, 0, 2) = Avg3(j, I, x); - Dst(dst, stride, 0, 1) = Dst(dst, stride, 1, 3) = Avg3(I, x, a); + Dst(dst, stride, 0, 3) = Avg3(k, j, i); + Dst(dst, stride, 0, 2) = Avg3(j, i, x); + Dst(dst, stride, 0, 1) = Dst(dst, stride, 1, 3) = Avg3(i, x, a); Dst(dst, stride, 1, 1) = Dst(dst, stride, 2, 3) = Avg3(x, a, b); Dst(dst, stride, 2, 1) = Dst(dst, stride, 3, 3) = Avg3(a, b, c); Dst(dst, stride, 3, 1) = Avg3(b, c, d); } - public static unsafe void HighbdD135Predictor4x4(ushort* dst, int stride, ushort* above, ushort* left, int bd) + public static unsafe void HighbdD135Predictor4X4(ushort* dst, int stride, ushort* above, ushort* left, int bd) { - ushort I = left[0]; + ushort i = left[0]; ushort j = left[1]; ushort k = left[2]; ushort l = left[3]; @@ -1344,17 +1348,18 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp ushort c = above[2]; ushort d = above[3]; Dst(dst, stride, 0, 3) = Avg3(j, k, l); - Dst(dst, stride, 1, 3) = Dst(dst, stride, 0, 2) = Avg3(I, j, k); - Dst(dst, stride, 2, 3) = Dst(dst, stride, 1, 2) = Dst(dst, stride, 0, 1) = Avg3(x, I, j); - Dst(dst, stride, 3, 3) = Dst(dst, stride, 2, 2) = Dst(dst, stride, 1, 1) = Dst(dst, stride, 0, 0) = Avg3(a, x, I); + Dst(dst, stride, 1, 3) = Dst(dst, stride, 0, 2) = Avg3(i, j, k); + Dst(dst, stride, 2, 3) = Dst(dst, stride, 1, 2) = Dst(dst, stride, 0, 1) = Avg3(x, i, j); + Dst(dst, stride, 3, 3) = + Dst(dst, stride, 2, 2) = Dst(dst, stride, 1, 1) = Dst(dst, stride, 0, 0) = Avg3(a, x, i); Dst(dst, stride, 3, 2) = Dst(dst, stride, 2, 1) = Dst(dst, stride, 1, 0) = Avg3(b, a, x); Dst(dst, stride, 3, 1) = Dst(dst, stride, 2, 0) = Avg3(c, b, a); Dst(dst, stride, 3, 0) = Avg3(d, c, b); } - public static unsafe void HighbdD153Predictor4x4(ushort* dst, int stride, ushort* above, ushort* left, int bd) + public static unsafe void HighbdD153Predictor4X4(ushort* dst, int stride, ushort* above, ushort* left, int bd) { - ushort I = left[0]; + ushort i = left[0]; ushort j = left[1]; ushort k = left[2]; ushort l = left[3]; @@ -1363,16 +1368,16 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp ushort b = above[1]; ushort c = above[2]; - Dst(dst, stride, 0, 0) = Dst(dst, stride, 2, 1) = Avg2(I, x); - Dst(dst, stride, 0, 1) = Dst(dst, stride, 2, 2) = Avg2(j, I); + Dst(dst, stride, 0, 0) = Dst(dst, stride, 2, 1) = Avg2(i, x); + Dst(dst, stride, 0, 1) = Dst(dst, stride, 2, 2) = Avg2(j, i); Dst(dst, stride, 0, 2) = Dst(dst, stride, 2, 3) = Avg2(k, j); Dst(dst, stride, 0, 3) = Avg2(l, k); Dst(dst, stride, 3, 0) = Avg3(a, b, c); Dst(dst, stride, 2, 0) = Avg3(x, a, b); - Dst(dst, stride, 1, 0) = Dst(dst, stride, 3, 1) = Avg3(I, x, a); - Dst(dst, stride, 1, 1) = Dst(dst, stride, 3, 2) = Avg3(j, I, x); - Dst(dst, stride, 1, 2) = Dst(dst, stride, 3, 3) = Avg3(k, j, I); + Dst(dst, stride, 1, 0) = Dst(dst, stride, 3, 1) = Avg3(i, x, a); + Dst(dst, stride, 1, 1) = Dst(dst, stride, 3, 2) = Avg3(j, i, x); + Dst(dst, stride, 1, 2) = Dst(dst, stride, 3, 3) = Avg3(k, j, i); Dst(dst, stride, 1, 3) = Avg3(l, k, j); } } diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/InvTxfm.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/InvTxfm.cs index c4f45bfda..a36ea2dcf 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/InvTxfm.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/InvTxfm.cs @@ -1,4 +1,4 @@ -using Ryujinx.Graphics.Nvdec.Vp9.Common; +using Ryujinx.Graphics.Nvdec.Vp9.Common; using System; using System.Diagnostics; using System.Runtime.CompilerServices; @@ -10,13 +10,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp { // 12 signal input bits + 7 2D forward transform amplify bits + 5 1D inverse // transform amplify bits + 1 bit for contingency in rounding and quantizing - private const int HighbdValidTxfmMagnitudeRange = (1 << 25); + private const int HighbdValidTxfmMagnitudeRange = 1 << 25; [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int DetectInvalidHighbdInput(ReadOnlySpan input, int size) { - int i; - for (i = 0; i < size; ++i) + for (int i = 0; i < size; ++i) { if (Math.Abs(input[i]) >= HighbdValidTxfmMagnitudeRange) { @@ -35,7 +34,6 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp // of this range for invalid/corrupt VP9 streams. Debug.Assert(short.MinValue <= input); Debug.Assert(input <= short.MaxValue); - return input; } @@ -71,7 +69,6 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp public static byte ClipPixelAdd(byte dest, long trans) { trans = WrapLow(trans); - return BitUtils.ClipPixel(dest + (int)trans); } @@ -79,7 +76,6 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp public static ushort HighbdClipPixelAdd(ushort dest, long trans, int bd) { trans = HighbdWrapLow(trans, bd); - return BitUtils.ClipPixelHighbd(dest + (int)trans, bd); } @@ -87,22 +83,21 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp private static long DctConstRoundShift(long input) { long rv = BitUtils.RoundPowerOfTwo(input, DctConstBits); - return rv; } [SkipLocalsInit] - public static void Iwht4x416Add(ReadOnlySpan input, Span dest, int stride) + public static void Iwht4X416Add(ReadOnlySpan input, Span dest, int stride) { /* 4-point reversible, orthonormal inverse Walsh-Hadamard in 3.5 adds, 0.5 shifts per pixel. */ - int i; + Span output = stackalloc int[16]; long a1, b1, c1, d1, e1; ReadOnlySpan ip = input; Span op = output; - for (i = 0; i < 4; i++) + for (int i = 0; i < 4; i++) { a1 = ip[0] >> UnitQuantShift; c1 = ip[1] >> UnitQuantShift; @@ -119,12 +114,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp op[1] = WrapLow(b1); op[2] = WrapLow(c1); op[3] = WrapLow(d1); - ip = ip[4..]; - op = op[4..]; + ip = ip.Slice(4); + op = op.Slice(4); } Span ip2 = output; - for (i = 0; i < 4; i++) + for (int i = 0; i < 4; i++) { a1 = ip2[4 * 0]; c1 = ip2[4 * 1]; @@ -142,15 +137,14 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp dest[stride * 2] = ClipPixelAdd(dest[stride * 2], WrapLow(c1)); dest[stride * 3] = ClipPixelAdd(dest[stride * 3], WrapLow(d1)); - ip2 = ip2[1..]; - dest = dest[1..]; + ip2 = ip2.Slice(1); + dest = dest.Slice(1); } } [SkipLocalsInit] - public static void Iwht4x41Add(ReadOnlySpan input, Span dest, int stride) + public static void Iwht4X41Add(ReadOnlySpan input, Span dest, int stride) { - int i; long a1, e1; Span tmp = stackalloc int[4]; ReadOnlySpan ip = input; @@ -163,7 +157,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp op[1] = op[2] = op[3] = WrapLow(e1); Span ip2 = tmp; - for (i = 0; i < 4; i++) + for (int i = 0; i < 4; i++) { e1 = ip2[0] >> 1; a1 = ip2[0] - e1; @@ -171,8 +165,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp dest[stride * 1] = ClipPixelAdd(dest[stride * 1], e1); dest[stride * 2] = ClipPixelAdd(dest[stride * 2], e1); dest[stride * 3] = ClipPixelAdd(dest[stride * 3], e1); - ip2 = ip2[1..]; - dest = dest[1..]; + ip2 = ip2.Slice(1); + dest = dest.Slice(1); } } @@ -186,25 +180,24 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp if ((x0 | x1 | x2 | x3) == 0) { - output[..4].Clear(); - + output.Slice(0, 4).Clear(); return; } // 32-bit result is enough for the following multiplications. - s0 = SinPi1_9 * x0; - s1 = SinPi2_9 * x0; - s2 = SinPi3_9 * x1; - s3 = SinPi4_9 * x2; - s4 = SinPi1_9 * x2; - s5 = SinPi2_9 * x3; - s6 = SinPi4_9 * x3; + s0 = SinPi19 * x0; + s1 = SinPi29 * x0; + s2 = SinPi39 * x1; + s3 = SinPi49 * x2; + s4 = SinPi19 * x2; + s5 = SinPi29 * x3; + s6 = SinPi49 * x3; s7 = WrapLow(x0 - x2 + x3); s0 = s0 + s3 + s5; s1 = s1 - s4 - s6; s3 = s2; - s2 = SinPi3_9 * s7; + s2 = SinPi39 * s7; // 1-D transform scaling factor is sqrt(2). // The overall dynamic range is 14b (input) + 14b (multiplication scaling) @@ -223,12 +216,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp long temp1, temp2; // stage 1 - temp1 = ((short)input[0] + (short)input[2]) * CosPi16_64; - temp2 = ((short)input[0] - (short)input[2]) * CosPi16_64; + temp1 = ((short)input[0] + (short)input[2]) * CosPi1664; + temp2 = ((short)input[0] - (short)input[2]) * CosPi1664; step[0] = (short)WrapLow(DctConstRoundShift(temp1)); step[1] = (short)WrapLow(DctConstRoundShift(temp2)); - temp1 = (short)input[1] * CosPi24_64 - (short)input[3] * CosPi8_64; - temp2 = (short)input[1] * CosPi8_64 + (short)input[3] * CosPi24_64; + temp1 = ((short)input[1] * CosPi2464) - ((short)input[3] * CosPi864); + temp2 = ((short)input[1] * CosPi864) + ((short)input[3] * CosPi2464); step[2] = (short)WrapLow(DctConstRoundShift(temp1)); step[3] = (short)WrapLow(DctConstRoundShift(temp2)); @@ -240,54 +233,53 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp } [SkipLocalsInit] - public static void Idct4x416Add(ReadOnlySpan input, Span dest, int stride) + public static void Idct4X416Add(ReadOnlySpan input, Span dest, int stride) { - int i, j; Span output = stackalloc int[4 * 4]; Span outptr = output; Span tempIn = stackalloc int[4]; Span tempOut = stackalloc int[4]; // Rows - for (i = 0; i < 4; ++i) + for (int i = 0; i < 4; ++i) { Idct4(input, outptr); - input = input[4..]; - outptr = outptr[4..]; + input = input.Slice(4); + outptr = outptr.Slice(4); } // Columns - for (i = 0; i < 4; ++i) + for (int i = 0; i < 4; ++i) { - for (j = 0; j < 4; ++j) + for (int j = 0; j < 4; ++j) { - tempIn[j] = output[j * 4 + i]; + tempIn[j] = output[(j * 4) + i]; } Idct4(tempIn, tempOut); - for (j = 0; j < 4; ++j) + for (int j = 0; j < 4; ++j) { - dest[j * stride + i] = ClipPixelAdd(dest[j * stride + i], BitUtils.RoundPowerOfTwo(tempOut[j], 4)); + dest[(j * stride) + i] = + ClipPixelAdd(dest[(j * stride) + i], BitUtils.RoundPowerOfTwo(tempOut[j], 4)); } } } - public static void Idct4x41Add(ReadOnlySpan input, Span dest, int stride) + public static void Idct4X41Add(ReadOnlySpan input, Span dest, int stride) { - int i; long a1; - int output = WrapLow(DctConstRoundShift((short)input[0] * CosPi16_64)); + int output = WrapLow(DctConstRoundShift((short)input[0] * CosPi1664)); - output = WrapLow(DctConstRoundShift(output * CosPi16_64)); + output = WrapLow(DctConstRoundShift(output * CosPi1664)); a1 = BitUtils.RoundPowerOfTwo(output, 4); - for (i = 0; i < 4; i++) + for (int i = 0; i < 4; i++) { dest[0] = ClipPixelAdd(dest[0], a1); dest[1] = ClipPixelAdd(dest[1], a1); dest[2] = ClipPixelAdd(dest[2], a1); dest[3] = ClipPixelAdd(dest[3], a1); - dest = dest[stride..]; + dest = dest.Slice(stride); } } @@ -305,20 +297,19 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp if ((x0 | x1 | x2 | x3 | x4 | x5 | x6 | x7) == 0) { - output[..8].Clear(); - + output.Slice(0, 8).Clear(); return; } // stage 1 - s0 = (int)(CosPi2_64 * x0 + CosPi30_64 * x1); - s1 = (int)(CosPi30_64 * x0 - CosPi2_64 * x1); - s2 = (int)(CosPi10_64 * x2 + CosPi22_64 * x3); - s3 = (int)(CosPi22_64 * x2 - CosPi10_64 * x3); - s4 = (int)(CosPi18_64 * x4 + CosPi14_64 * x5); - s5 = (int)(CosPi14_64 * x4 - CosPi18_64 * x5); - s6 = (int)(CosPi26_64 * x6 + CosPi6_64 * x7); - s7 = (int)(CosPi6_64 * x6 - CosPi26_64 * x7); + s0 = (int)((CosPi264 * x0) + (CosPi3064 * x1)); + s1 = (int)((CosPi3064 * x0) - (CosPi264 * x1)); + s2 = (int)((CosPi1064 * x2) + (CosPi2264 * x3)); + s3 = (int)((CosPi2264 * x2) - (CosPi1064 * x3)); + s4 = (int)((CosPi1864 * x4) + (CosPi1464 * x5)); + s5 = (int)((CosPi1464 * x4) - (CosPi1864 * x5)); + s6 = (int)((CosPi2664 * x6) + (CosPi664 * x7)); + s7 = (int)((CosPi664 * x6) - (CosPi2664 * x7)); x0 = WrapLow(DctConstRoundShift(s0 + s4)); x1 = WrapLow(DctConstRoundShift(s1 + s5)); @@ -334,10 +325,10 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp s1 = (int)x1; s2 = (int)x2; s3 = (int)x3; - s4 = (int)(CosPi8_64 * x4 + CosPi24_64 * x5); - s5 = (int)(CosPi24_64 * x4 - CosPi8_64 * x5); - s6 = (int)(-CosPi24_64 * x6 + CosPi8_64 * x7); - s7 = (int)(CosPi8_64 * x6 + CosPi24_64 * x7); + s4 = (int)((CosPi864 * x4) + (CosPi2464 * x5)); + s5 = (int)((CosPi2464 * x4) - (CosPi864 * x5)); + s6 = (int)((-CosPi2464 * x6) + (CosPi864 * x7)); + s7 = (int)((CosPi864 * x6) + (CosPi2464 * x7)); x0 = WrapLow(s0 + s2); x1 = WrapLow(s1 + s3); @@ -349,10 +340,10 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp x7 = WrapLow(DctConstRoundShift(s5 - s7)); // stage 3 - s2 = (int)(CosPi16_64 * (x2 + x3)); - s3 = (int)(CosPi16_64 * (x2 - x3)); - s6 = (int)(CosPi16_64 * (x6 + x7)); - s7 = (int)(CosPi16_64 * (x6 - x7)); + s2 = (int)(CosPi1664 * (x2 + x3)); + s3 = (int)(CosPi1664 * (x2 - x3)); + s6 = (int)(CosPi1664 * (x6 + x7)); + s7 = (int)(CosPi1664 * (x6 - x7)); x2 = WrapLow(DctConstRoundShift(s2)); x3 = WrapLow(DctConstRoundShift(s3)); @@ -381,22 +372,22 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp step1[2] = (short)input[4]; step1[1] = (short)input[2]; step1[3] = (short)input[6]; - temp1 = (short)input[1] * CosPi28_64 - (short)input[7] * CosPi4_64; - temp2 = (short)input[1] * CosPi4_64 + (short)input[7] * CosPi28_64; + temp1 = ((short)input[1] * CosPi2864) - ((short)input[7] * CosPi464); + temp2 = ((short)input[1] * CosPi464) + ((short)input[7] * CosPi2864); step1[4] = (short)WrapLow(DctConstRoundShift(temp1)); step1[7] = (short)WrapLow(DctConstRoundShift(temp2)); - temp1 = (short)input[5] * CosPi12_64 - (short)input[3] * CosPi20_64; - temp2 = (short)input[5] * CosPi20_64 + (short)input[3] * CosPi12_64; + temp1 = ((short)input[5] * CosPi1264) - ((short)input[3] * CosPi2064); + temp2 = ((short)input[5] * CosPi2064) + ((short)input[3] * CosPi1264); step1[5] = (short)WrapLow(DctConstRoundShift(temp1)); step1[6] = (short)WrapLow(DctConstRoundShift(temp2)); // stage 2 - temp1 = (step1[0] + step1[2]) * CosPi16_64; - temp2 = (step1[0] - step1[2]) * CosPi16_64; + temp1 = (step1[0] + step1[2]) * CosPi1664; + temp2 = (step1[0] - step1[2]) * CosPi1664; step2[0] = (short)WrapLow(DctConstRoundShift(temp1)); step2[1] = (short)WrapLow(DctConstRoundShift(temp2)); - temp1 = step1[1] * CosPi24_64 - step1[3] * CosPi8_64; - temp2 = step1[1] * CosPi8_64 + step1[3] * CosPi24_64; + temp1 = (step1[1] * CosPi2464) - (step1[3] * CosPi864); + temp2 = (step1[1] * CosPi864) + (step1[3] * CosPi2464); step2[2] = (short)WrapLow(DctConstRoundShift(temp1)); step2[3] = (short)WrapLow(DctConstRoundShift(temp2)); step2[4] = (short)WrapLow(step1[4] + step1[5]); @@ -410,8 +401,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp step1[2] = (short)WrapLow(step2[1] - step2[2]); step1[3] = (short)WrapLow(step2[0] - step2[3]); step1[4] = step2[4]; - temp1 = (step2[6] - step2[5]) * CosPi16_64; - temp2 = (step2[5] + step2[6]) * CosPi16_64; + temp1 = (step2[6] - step2[5]) * CosPi1664; + temp2 = (step2[5] + step2[6]) * CosPi1664; step1[5] = (short)WrapLow(DctConstRoundShift(temp1)); step1[6] = (short)WrapLow(DctConstRoundShift(temp2)); step1[7] = step2[7]; @@ -428,43 +419,41 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp } [SkipLocalsInit] - public static void Idct8x864Add(ReadOnlySpan input, Span dest, int stride) + public static void Idct8X864Add(ReadOnlySpan input, Span dest, int stride) { - int i, j; Span output = stackalloc int[8 * 8]; Span outptr = output; Span tempIn = stackalloc int[8]; Span tempOut = stackalloc int[8]; // First transform rows - for (i = 0; i < 8; ++i) + for (int i = 0; i < 8; ++i) { Idct8(input, outptr); - input = input[8..]; - outptr = outptr[8..]; + input = input.Slice(8); + outptr = outptr.Slice(8); } // Then transform columns - for (i = 0; i < 8; ++i) + for (int i = 0; i < 8; ++i) { - for (j = 0; j < 8; ++j) + for (int j = 0; j < 8; ++j) { - tempIn[j] = output[j * 8 + i]; + tempIn[j] = output[(j * 8) + i]; } Idct8(tempIn, tempOut); - for (j = 0; j < 8; ++j) + for (int j = 0; j < 8; ++j) { - dest[j * stride + i] = ClipPixelAdd(dest[j * stride + i], - BitUtils.RoundPowerOfTwo(tempOut[j], 5)); + dest[(j * stride) + i] = ClipPixelAdd(dest[(j * stride) + i], + BitUtils.RoundPowerOfTwo(tempOut[j], 5)); } } } [SkipLocalsInit] - public static void Idct8x812Add(ReadOnlySpan input, Span dest, int stride) + public static void Idct8X812Add(ReadOnlySpan input, Span dest, int stride) { - int i, j; Span output = stackalloc int[8 * 8]; Span outptr = output; Span tempIn = stackalloc int[8]; @@ -474,45 +463,45 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp // First transform rows // Only first 4 row has non-zero coefs - for (i = 0; i < 4; ++i) + for (int i = 0; i < 4; ++i) { Idct8(input, outptr); - input = input[8..]; - outptr = outptr[8..]; + input = input.Slice(8); + outptr = outptr.Slice(8); } // Then transform columns - for (i = 0; i < 8; ++i) + for (int i = 0; i < 8; ++i) { - for (j = 0; j < 8; ++j) + for (int j = 0; j < 8; ++j) { - tempIn[j] = output[j * 8 + i]; + tempIn[j] = output[(j * 8) + i]; } Idct8(tempIn, tempOut); - for (j = 0; j < 8; ++j) + for (int j = 0; j < 8; ++j) { - dest[j * stride + i] = ClipPixelAdd(dest[j * stride + i], BitUtils.RoundPowerOfTwo(tempOut[j], 5)); + dest[(j * stride) + i] = + ClipPixelAdd(dest[(j * stride) + i], BitUtils.RoundPowerOfTwo(tempOut[j], 5)); } } } - public static void Idct8x81Add(ReadOnlySpan input, Span dest, int stride) + public static void Idct8X81Add(ReadOnlySpan input, Span dest, int stride) { - int i, j; long a1; - int output = WrapLow(DctConstRoundShift((short)input[0] * CosPi16_64)); + int output = WrapLow(DctConstRoundShift((short)input[0] * CosPi1664)); - output = WrapLow(DctConstRoundShift(output * CosPi16_64)); + output = WrapLow(DctConstRoundShift(output * CosPi1664)); a1 = BitUtils.RoundPowerOfTwo(output, 5); - for (j = 0; j < 8; ++j) + for (int j = 0; j < 8; ++j) { - for (i = 0; i < 8; ++i) + for (int i = 0; i < 8; ++i) { dest[i] = ClipPixelAdd(dest[i], a1); } - dest = dest[stride..]; + dest = dest.Slice(stride); } } @@ -539,28 +528,27 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp if ((x0 | x1 | x2 | x3 | x4 | x5 | x6 | x7 | x8 | x9 | x10 | x11 | x12 | x13 | x14 | x15) == 0) { - output[..16].Clear(); - + output.Slice(0, 16).Clear(); return; } // stage 1 - s0 = x0 * CosPi1_64 + x1 * CosPi31_64; - s1 = x0 * CosPi31_64 - x1 * CosPi1_64; - s2 = x2 * CosPi5_64 + x3 * CosPi27_64; - s3 = x2 * CosPi27_64 - x3 * CosPi5_64; - s4 = x4 * CosPi9_64 + x5 * CosPi23_64; - s5 = x4 * CosPi23_64 - x5 * CosPi9_64; - s6 = x6 * CosPi13_64 + x7 * CosPi19_64; - s7 = x6 * CosPi19_64 - x7 * CosPi13_64; - s8 = x8 * CosPi17_64 + x9 * CosPi15_64; - s9 = x8 * CosPi15_64 - x9 * CosPi17_64; - s10 = x10 * CosPi21_64 + x11 * CosPi11_64; - s11 = x10 * CosPi11_64 - x11 * CosPi21_64; - s12 = x12 * CosPi25_64 + x13 * CosPi7_64; - s13 = x12 * CosPi7_64 - x13 * CosPi25_64; - s14 = x14 * CosPi29_64 + x15 * CosPi3_64; - s15 = x14 * CosPi3_64 - x15 * CosPi29_64; + s0 = (x0 * CosPi164) + (x1 * CosPi3164); + s1 = (x0 * CosPi3164) - (x1 * CosPi164); + s2 = (x2 * CosPi564) + (x3 * CosPi2764); + s3 = (x2 * CosPi2764) - (x3 * CosPi564); + s4 = (x4 * CosPi964) + (x5 * CosPi2364); + s5 = (x4 * CosPi2364) - (x5 * CosPi964); + s6 = (x6 * CosPi1364) + (x7 * CosPi1964); + s7 = (x6 * CosPi1964) - (x7 * CosPi1364); + s8 = (x8 * CosPi1764) + (x9 * CosPi1564); + s9 = (x8 * CosPi1564) - (x9 * CosPi1764); + s10 = (x10 * CosPi2164) + (x11 * CosPi1164); + s11 = (x10 * CosPi1164) - (x11 * CosPi2164); + s12 = (x12 * CosPi2564) + (x13 * CosPi764); + s13 = (x12 * CosPi764) - (x13 * CosPi2564); + s14 = (x14 * CosPi2964) + (x15 * CosPi364); + s15 = (x14 * CosPi364) - (x15 * CosPi2964); x0 = WrapLow(DctConstRoundShift(s0 + s8)); x1 = WrapLow(DctConstRoundShift(s1 + s9)); @@ -588,14 +576,14 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp s5 = x5; s6 = x6; s7 = x7; - s8 = x8 * CosPi4_64 + x9 * CosPi28_64; - s9 = x8 * CosPi28_64 - x9 * CosPi4_64; - s10 = x10 * CosPi20_64 + x11 * CosPi12_64; - s11 = x10 * CosPi12_64 - x11 * CosPi20_64; - s12 = -x12 * CosPi28_64 + x13 * CosPi4_64; - s13 = x12 * CosPi4_64 + x13 * CosPi28_64; - s14 = -x14 * CosPi12_64 + x15 * CosPi20_64; - s15 = x14 * CosPi20_64 + x15 * CosPi12_64; + s8 = (x8 * CosPi464) + (x9 * CosPi2864); + s9 = (x8 * CosPi2864) - (x9 * CosPi464); + s10 = (x10 * CosPi2064) + (x11 * CosPi1264); + s11 = (x10 * CosPi1264) - (x11 * CosPi2064); + s12 = (-x12 * CosPi2864) + (x13 * CosPi464); + s13 = (x12 * CosPi464) + (x13 * CosPi2864); + s14 = (-x14 * CosPi1264) + (x15 * CosPi2064); + s15 = (x14 * CosPi2064) + (x15 * CosPi1264); x0 = WrapLow(s0 + s4); x1 = WrapLow(s1 + s5); @@ -619,18 +607,18 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp s1 = x1; s2 = x2; s3 = x3; - s4 = x4 * CosPi8_64 + x5 * CosPi24_64; - s5 = x4 * CosPi24_64 - x5 * CosPi8_64; - s6 = -x6 * CosPi24_64 + x7 * CosPi8_64; - s7 = x6 * CosPi8_64 + x7 * CosPi24_64; + s4 = (x4 * CosPi864) + (x5 * CosPi2464); + s5 = (x4 * CosPi2464) - (x5 * CosPi864); + s6 = (-x6 * CosPi2464) + (x7 * CosPi864); + s7 = (x6 * CosPi864) + (x7 * CosPi2464); s8 = x8; s9 = x9; s10 = x10; s11 = x11; - s12 = x12 * CosPi8_64 + x13 * CosPi24_64; - s13 = x12 * CosPi24_64 - x13 * CosPi8_64; - s14 = -x14 * CosPi24_64 + x15 * CosPi8_64; - s15 = x14 * CosPi8_64 + x15 * CosPi24_64; + s12 = (x12 * CosPi864) + (x13 * CosPi2464); + s13 = (x12 * CosPi2464) - (x13 * CosPi864); + s14 = (-x14 * CosPi2464) + (x15 * CosPi864); + s15 = (x14 * CosPi864) + (x15 * CosPi2464); x0 = WrapLow(s0 + s2); x1 = WrapLow(s1 + s3); @@ -650,14 +638,14 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp x15 = WrapLow(DctConstRoundShift(s13 - s15)); // stage 4 - s2 = (-CosPi16_64) * (x2 + x3); - s3 = CosPi16_64 * (x2 - x3); - s6 = CosPi16_64 * (x6 + x7); - s7 = CosPi16_64 * (-x6 + x7); - s10 = CosPi16_64 * (x10 + x11); - s11 = CosPi16_64 * (-x10 + x11); - s14 = (-CosPi16_64) * (x14 + x15); - s15 = CosPi16_64 * (x14 - x15); + s2 = -CosPi1664 * (x2 + x3); + s3 = CosPi1664 * (x2 - x3); + s6 = CosPi1664 * (x6 + x7); + s7 = CosPi1664 * (-x6 + x7); + s10 = CosPi1664 * (x10 + x11); + s11 = CosPi1664 * (-x10 + x11); + s14 = -CosPi1664 * (x14 + x15); + s15 = CosPi1664 * (x14 - x15); x2 = WrapLow(DctConstRoundShift(s2)); x3 = WrapLow(DctConstRoundShift(s3)); @@ -721,23 +709,23 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp step2[6] = step1[6]; step2[7] = step1[7]; - temp1 = step1[8] * CosPi30_64 - step1[15] * CosPi2_64; - temp2 = step1[8] * CosPi2_64 + step1[15] * CosPi30_64; + temp1 = (step1[8] * CosPi3064) - (step1[15] * CosPi264); + temp2 = (step1[8] * CosPi264) + (step1[15] * CosPi3064); step2[8] = (short)WrapLow(DctConstRoundShift(temp1)); step2[15] = (short)WrapLow(DctConstRoundShift(temp2)); - temp1 = step1[9] * CosPi14_64 - step1[14] * CosPi18_64; - temp2 = step1[9] * CosPi18_64 + step1[14] * CosPi14_64; + temp1 = (step1[9] * CosPi1464) - (step1[14] * CosPi1864); + temp2 = (step1[9] * CosPi1864) + (step1[14] * CosPi1464); step2[9] = (short)WrapLow(DctConstRoundShift(temp1)); step2[14] = (short)WrapLow(DctConstRoundShift(temp2)); - temp1 = step1[10] * CosPi22_64 - step1[13] * CosPi10_64; - temp2 = step1[10] * CosPi10_64 + step1[13] * CosPi22_64; + temp1 = (step1[10] * CosPi2264) - (step1[13] * CosPi1064); + temp2 = (step1[10] * CosPi1064) + (step1[13] * CosPi2264); step2[10] = (short)WrapLow(DctConstRoundShift(temp1)); step2[13] = (short)WrapLow(DctConstRoundShift(temp2)); - temp1 = step1[11] * CosPi6_64 - step1[12] * CosPi26_64; - temp2 = step1[11] * CosPi26_64 + step1[12] * CosPi6_64; + temp1 = (step1[11] * CosPi664) - (step1[12] * CosPi2664); + temp2 = (step1[11] * CosPi2664) + (step1[12] * CosPi664); step2[11] = (short)WrapLow(DctConstRoundShift(temp1)); step2[12] = (short)WrapLow(DctConstRoundShift(temp2)); @@ -747,12 +735,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp step1[2] = step2[2]; step1[3] = step2[3]; - temp1 = step2[4] * CosPi28_64 - step2[7] * CosPi4_64; - temp2 = step2[4] * CosPi4_64 + step2[7] * CosPi28_64; + temp1 = (step2[4] * CosPi2864) - (step2[7] * CosPi464); + temp2 = (step2[4] * CosPi464) + (step2[7] * CosPi2864); step1[4] = (short)WrapLow(DctConstRoundShift(temp1)); step1[7] = (short)WrapLow(DctConstRoundShift(temp2)); - temp1 = step2[5] * CosPi12_64 - step2[6] * CosPi20_64; - temp2 = step2[5] * CosPi20_64 + step2[6] * CosPi12_64; + temp1 = (step2[5] * CosPi1264) - (step2[6] * CosPi2064); + temp2 = (step2[5] * CosPi2064) + (step2[6] * CosPi1264); step1[5] = (short)WrapLow(DctConstRoundShift(temp1)); step1[6] = (short)WrapLow(DctConstRoundShift(temp2)); @@ -766,12 +754,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp step1[15] = (short)WrapLow(step2[14] + step2[15]); // stage 4 - temp1 = (step1[0] + step1[1]) * CosPi16_64; - temp2 = (step1[0] - step1[1]) * CosPi16_64; + temp1 = (step1[0] + step1[1]) * CosPi1664; + temp2 = (step1[0] - step1[1]) * CosPi1664; step2[0] = (short)WrapLow(DctConstRoundShift(temp1)); step2[1] = (short)WrapLow(DctConstRoundShift(temp2)); - temp1 = step1[2] * CosPi24_64 - step1[3] * CosPi8_64; - temp2 = step1[2] * CosPi8_64 + step1[3] * CosPi24_64; + temp1 = (step1[2] * CosPi2464) - (step1[3] * CosPi864); + temp2 = (step1[2] * CosPi864) + (step1[3] * CosPi2464); step2[2] = (short)WrapLow(DctConstRoundShift(temp1)); step2[3] = (short)WrapLow(DctConstRoundShift(temp2)); step2[4] = (short)WrapLow(step1[4] + step1[5]); @@ -781,12 +769,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp step2[8] = step1[8]; step2[15] = step1[15]; - temp1 = -step1[9] * CosPi8_64 + step1[14] * CosPi24_64; - temp2 = step1[9] * CosPi24_64 + step1[14] * CosPi8_64; + temp1 = (-step1[9] * CosPi864) + (step1[14] * CosPi2464); + temp2 = (step1[9] * CosPi2464) + (step1[14] * CosPi864); step2[9] = (short)WrapLow(DctConstRoundShift(temp1)); step2[14] = (short)WrapLow(DctConstRoundShift(temp2)); - temp1 = -step1[10] * CosPi24_64 - step1[13] * CosPi8_64; - temp2 = -step1[10] * CosPi8_64 + step1[13] * CosPi24_64; + temp1 = (-step1[10] * CosPi2464) - (step1[13] * CosPi864); + temp2 = (-step1[10] * CosPi864) + (step1[13] * CosPi2464); step2[10] = (short)WrapLow(DctConstRoundShift(temp1)); step2[13] = (short)WrapLow(DctConstRoundShift(temp2)); step2[11] = step1[11]; @@ -798,8 +786,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp step1[2] = (short)WrapLow(step2[1] - step2[2]); step1[3] = (short)WrapLow(step2[0] - step2[3]); step1[4] = step2[4]; - temp1 = (step2[6] - step2[5]) * CosPi16_64; - temp2 = (step2[5] + step2[6]) * CosPi16_64; + temp1 = (step2[6] - step2[5]) * CosPi1664; + temp2 = (step2[5] + step2[6]) * CosPi1664; step1[5] = (short)WrapLow(DctConstRoundShift(temp1)); step1[6] = (short)WrapLow(DctConstRoundShift(temp2)); step1[7] = step2[7]; @@ -824,12 +812,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp step2[7] = (short)WrapLow(step1[0] - step1[7]); step2[8] = step1[8]; step2[9] = step1[9]; - temp1 = (-step1[10] + step1[13]) * CosPi16_64; - temp2 = (step1[10] + step1[13]) * CosPi16_64; + temp1 = (-step1[10] + step1[13]) * CosPi1664; + temp2 = (step1[10] + step1[13]) * CosPi1664; step2[10] = (short)WrapLow(DctConstRoundShift(temp1)); step2[13] = (short)WrapLow(DctConstRoundShift(temp2)); - temp1 = (-step1[11] + step1[12]) * CosPi16_64; - temp2 = (step1[11] + step1[12]) * CosPi16_64; + temp1 = (-step1[11] + step1[12]) * CosPi1664; + temp2 = (step1[11] + step1[12]) * CosPi1664; step2[11] = (short)WrapLow(DctConstRoundShift(temp1)); step2[12] = (short)WrapLow(DctConstRoundShift(temp2)); step2[14] = step1[14]; @@ -855,42 +843,41 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp } [SkipLocalsInit] - public static void Idct16x16256Add(ReadOnlySpan input, Span dest, int stride) + public static void Idct16X16256Add(ReadOnlySpan input, Span dest, int stride) { - int i, j; Span output = stackalloc int[16 * 16]; Span outptr = output; Span tempIn = stackalloc int[16]; Span tempOut = stackalloc int[16]; // First transform rows - for (i = 0; i < 16; ++i) + for (int i = 0; i < 16; ++i) { Idct16(input, outptr); - input = input[16..]; - outptr = outptr[16..]; + input = input.Slice(16); + outptr = outptr.Slice(16); } // Then transform columns - for (i = 0; i < 16; ++i) + for (int i = 0; i < 16; ++i) { - for (j = 0; j < 16; ++j) + for (int j = 0; j < 16; ++j) { - tempIn[j] = output[j * 16 + i]; + tempIn[j] = output[(j * 16) + i]; } Idct16(tempIn, tempOut); - for (j = 0; j < 16; ++j) + for (int j = 0; j < 16; ++j) { - dest[j * stride + i] = ClipPixelAdd(dest[j * stride + i], BitUtils.RoundPowerOfTwo(tempOut[j], 6)); + dest[(j * stride) + i] = + ClipPixelAdd(dest[(j * stride) + i], BitUtils.RoundPowerOfTwo(tempOut[j], 6)); } } } [SkipLocalsInit] - public static void Idct16x1638Add(ReadOnlySpan input, Span dest, int stride) + public static void Idct16X1638Add(ReadOnlySpan input, Span dest, int stride) { - int i, j; Span output = stackalloc int[16 * 16]; Span outptr = output; Span tempIn = stackalloc int[16]; @@ -900,33 +887,33 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp // First transform rows. Since all non-zero dct coefficients are in // upper-left 8x8 area, we only need to calculate first 8 rows here. - for (i = 0; i < 8; ++i) + for (int i = 0; i < 8; ++i) { Idct16(input, outptr); - input = input[16..]; - outptr = outptr[16..]; + input = input.Slice(16); + outptr = outptr.Slice(16); } // Then transform columns - for (i = 0; i < 16; ++i) + for (int i = 0; i < 16; ++i) { - for (j = 0; j < 16; ++j) + for (int j = 0; j < 16; ++j) { - tempIn[j] = output[j * 16 + i]; + tempIn[j] = output[(j * 16) + i]; } Idct16(tempIn, tempOut); - for (j = 0; j < 16; ++j) + for (int j = 0; j < 16; ++j) { - dest[j * stride + i] = ClipPixelAdd(dest[j * stride + i], BitUtils.RoundPowerOfTwo(tempOut[j], 6)); + dest[(j * stride) + i] = + ClipPixelAdd(dest[(j * stride) + i], BitUtils.RoundPowerOfTwo(tempOut[j], 6)); } } } [SkipLocalsInit] - public static void Idct16x1610Add(ReadOnlySpan input, Span dest, int stride) + public static void Idct16X1610Add(ReadOnlySpan input, Span dest, int stride) { - int i, j; Span output = stackalloc int[16 * 16]; Span outptr = output; Span tempIn = stackalloc int[16]; @@ -936,45 +923,45 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp // First transform rows. Since all non-zero dct coefficients are in // upper-left 4x4 area, we only need to calculate first 4 rows here. - for (i = 0; i < 4; ++i) + for (int i = 0; i < 4; ++i) { Idct16(input, outptr); - input = input[16..]; - outptr = outptr[16..]; + input = input.Slice(16); + outptr = outptr.Slice(16); } // Then transform columns - for (i = 0; i < 16; ++i) + for (int i = 0; i < 16; ++i) { - for (j = 0; j < 16; ++j) + for (int j = 0; j < 16; ++j) { - tempIn[j] = output[j * 16 + i]; + tempIn[j] = output[(j * 16) + i]; } Idct16(tempIn, tempOut); - for (j = 0; j < 16; ++j) + for (int j = 0; j < 16; ++j) { - dest[j * stride + i] = ClipPixelAdd(dest[j * stride + i], BitUtils.RoundPowerOfTwo(tempOut[j], 6)); + dest[(j * stride) + i] = + ClipPixelAdd(dest[(j * stride) + i], BitUtils.RoundPowerOfTwo(tempOut[j], 6)); } } } - public static void Idct16x161Add(ReadOnlySpan input, Span dest, int stride) + public static void Idct16X161Add(ReadOnlySpan input, Span dest, int stride) { - int i, j; long a1; - int output = WrapLow(DctConstRoundShift((short)input[0] * CosPi16_64)); + int output = WrapLow(DctConstRoundShift((short)input[0] * CosPi1664)); - output = WrapLow(DctConstRoundShift(output * CosPi16_64)); + output = WrapLow(DctConstRoundShift(output * CosPi1664)); a1 = BitUtils.RoundPowerOfTwo(output, 6); - for (j = 0; j < 16; ++j) + for (int j = 0; j < 16; ++j) { - for (i = 0; i < 16; ++i) + for (int i = 0; i < 16; ++i) { dest[i] = ClipPixelAdd(dest[i], a1); } - dest = dest[stride..]; + dest = dest.Slice(stride); } } @@ -1003,43 +990,43 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp step1[14] = (short)input[14]; step1[15] = (short)input[30]; - temp1 = (short)input[1] * CosPi31_64 - (short)input[31] * CosPi1_64; - temp2 = (short)input[1] * CosPi1_64 + (short)input[31] * CosPi31_64; + temp1 = ((short)input[1] * CosPi3164) - ((short)input[31] * CosPi164); + temp2 = ((short)input[1] * CosPi164) + ((short)input[31] * CosPi3164); step1[16] = (short)WrapLow(DctConstRoundShift(temp1)); step1[31] = (short)WrapLow(DctConstRoundShift(temp2)); - temp1 = (short)input[17] * CosPi15_64 - (short)input[15] * CosPi17_64; - temp2 = (short)input[17] * CosPi17_64 + (short)input[15] * CosPi15_64; + temp1 = ((short)input[17] * CosPi1564) - ((short)input[15] * CosPi1764); + temp2 = ((short)input[17] * CosPi1764) + ((short)input[15] * CosPi1564); step1[17] = (short)WrapLow(DctConstRoundShift(temp1)); step1[30] = (short)WrapLow(DctConstRoundShift(temp2)); - temp1 = (short)input[9] * CosPi23_64 - (short)input[23] * CosPi9_64; - temp2 = (short)input[9] * CosPi9_64 + (short)input[23] * CosPi23_64; + temp1 = ((short)input[9] * CosPi2364) - ((short)input[23] * CosPi964); + temp2 = ((short)input[9] * CosPi964) + ((short)input[23] * CosPi2364); step1[18] = (short)WrapLow(DctConstRoundShift(temp1)); step1[29] = (short)WrapLow(DctConstRoundShift(temp2)); - temp1 = (short)input[25] * CosPi7_64 - (short)input[7] * CosPi25_64; - temp2 = (short)input[25] * CosPi25_64 + (short)input[7] * CosPi7_64; + temp1 = ((short)input[25] * CosPi764) - ((short)input[7] * CosPi2564); + temp2 = ((short)input[25] * CosPi2564) + ((short)input[7] * CosPi764); step1[19] = (short)WrapLow(DctConstRoundShift(temp1)); step1[28] = (short)WrapLow(DctConstRoundShift(temp2)); - temp1 = (short)input[5] * CosPi27_64 - (short)input[27] * CosPi5_64; - temp2 = (short)input[5] * CosPi5_64 + (short)input[27] * CosPi27_64; + temp1 = ((short)input[5] * CosPi2764) - ((short)input[27] * CosPi564); + temp2 = ((short)input[5] * CosPi564) + ((short)input[27] * CosPi2764); step1[20] = (short)WrapLow(DctConstRoundShift(temp1)); step1[27] = (short)WrapLow(DctConstRoundShift(temp2)); - temp1 = (short)input[21] * CosPi11_64 - (short)input[11] * CosPi21_64; - temp2 = (short)input[21] * CosPi21_64 + (short)input[11] * CosPi11_64; + temp1 = ((short)input[21] * CosPi1164) - ((short)input[11] * CosPi2164); + temp2 = ((short)input[21] * CosPi2164) + ((short)input[11] * CosPi1164); step1[21] = (short)WrapLow(DctConstRoundShift(temp1)); step1[26] = (short)WrapLow(DctConstRoundShift(temp2)); - temp1 = (short)input[13] * CosPi19_64 - (short)input[19] * CosPi13_64; - temp2 = (short)input[13] * CosPi13_64 + (short)input[19] * CosPi19_64; + temp1 = ((short)input[13] * CosPi1964) - ((short)input[19] * CosPi1364); + temp2 = ((short)input[13] * CosPi1364) + ((short)input[19] * CosPi1964); step1[22] = (short)WrapLow(DctConstRoundShift(temp1)); step1[25] = (short)WrapLow(DctConstRoundShift(temp2)); - temp1 = (short)input[29] * CosPi3_64 - (short)input[3] * CosPi29_64; - temp2 = (short)input[29] * CosPi29_64 + (short)input[3] * CosPi3_64; + temp1 = ((short)input[29] * CosPi364) - ((short)input[3] * CosPi2964); + temp2 = ((short)input[29] * CosPi2964) + ((short)input[3] * CosPi364); step1[23] = (short)WrapLow(DctConstRoundShift(temp1)); step1[24] = (short)WrapLow(DctConstRoundShift(temp2)); @@ -1053,23 +1040,23 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp step2[6] = step1[6]; step2[7] = step1[7]; - temp1 = step1[8] * CosPi30_64 - step1[15] * CosPi2_64; - temp2 = step1[8] * CosPi2_64 + step1[15] * CosPi30_64; + temp1 = (step1[8] * CosPi3064) - (step1[15] * CosPi264); + temp2 = (step1[8] * CosPi264) + (step1[15] * CosPi3064); step2[8] = (short)WrapLow(DctConstRoundShift(temp1)); step2[15] = (short)WrapLow(DctConstRoundShift(temp2)); - temp1 = step1[9] * CosPi14_64 - step1[14] * CosPi18_64; - temp2 = step1[9] * CosPi18_64 + step1[14] * CosPi14_64; + temp1 = (step1[9] * CosPi1464) - (step1[14] * CosPi1864); + temp2 = (step1[9] * CosPi1864) + (step1[14] * CosPi1464); step2[9] = (short)WrapLow(DctConstRoundShift(temp1)); step2[14] = (short)WrapLow(DctConstRoundShift(temp2)); - temp1 = step1[10] * CosPi22_64 - step1[13] * CosPi10_64; - temp2 = step1[10] * CosPi10_64 + step1[13] * CosPi22_64; + temp1 = (step1[10] * CosPi2264) - (step1[13] * CosPi1064); + temp2 = (step1[10] * CosPi1064) + (step1[13] * CosPi2264); step2[10] = (short)WrapLow(DctConstRoundShift(temp1)); step2[13] = (short)WrapLow(DctConstRoundShift(temp2)); - temp1 = step1[11] * CosPi6_64 - step1[12] * CosPi26_64; - temp2 = step1[11] * CosPi26_64 + step1[12] * CosPi6_64; + temp1 = (step1[11] * CosPi664) - (step1[12] * CosPi2664); + temp2 = (step1[11] * CosPi2664) + (step1[12] * CosPi664); step2[11] = (short)WrapLow(DctConstRoundShift(temp1)); step2[12] = (short)WrapLow(DctConstRoundShift(temp2)); @@ -1096,12 +1083,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp step1[2] = step2[2]; step1[3] = step2[3]; - temp1 = step2[4] * CosPi28_64 - step2[7] * CosPi4_64; - temp2 = step2[4] * CosPi4_64 + step2[7] * CosPi28_64; + temp1 = (step2[4] * CosPi2864) - (step2[7] * CosPi464); + temp2 = (step2[4] * CosPi464) + (step2[7] * CosPi2864); step1[4] = (short)WrapLow(DctConstRoundShift(temp1)); step1[7] = (short)WrapLow(DctConstRoundShift(temp2)); - temp1 = step2[5] * CosPi12_64 - step2[6] * CosPi20_64; - temp2 = step2[5] * CosPi20_64 + step2[6] * CosPi12_64; + temp1 = (step2[5] * CosPi1264) - (step2[6] * CosPi2064); + temp2 = (step2[5] * CosPi2064) + (step2[6] * CosPi1264); step1[5] = (short)WrapLow(DctConstRoundShift(temp1)); step1[6] = (short)WrapLow(DctConstRoundShift(temp2)); @@ -1116,22 +1103,22 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp step1[16] = step2[16]; step1[31] = step2[31]; - temp1 = -step2[17] * CosPi4_64 + step2[30] * CosPi28_64; - temp2 = step2[17] * CosPi28_64 + step2[30] * CosPi4_64; + temp1 = (-step2[17] * CosPi464) + (step2[30] * CosPi2864); + temp2 = (step2[17] * CosPi2864) + (step2[30] * CosPi464); step1[17] = (short)WrapLow(DctConstRoundShift(temp1)); step1[30] = (short)WrapLow(DctConstRoundShift(temp2)); - temp1 = -step2[18] * CosPi28_64 - step2[29] * CosPi4_64; - temp2 = -step2[18] * CosPi4_64 + step2[29] * CosPi28_64; + temp1 = (-step2[18] * CosPi2864) - (step2[29] * CosPi464); + temp2 = (-step2[18] * CosPi464) + (step2[29] * CosPi2864); step1[18] = (short)WrapLow(DctConstRoundShift(temp1)); step1[29] = (short)WrapLow(DctConstRoundShift(temp2)); step1[19] = step2[19]; step1[20] = step2[20]; - temp1 = -step2[21] * CosPi20_64 + step2[26] * CosPi12_64; - temp2 = step2[21] * CosPi12_64 + step2[26] * CosPi20_64; + temp1 = (-step2[21] * CosPi2064) + (step2[26] * CosPi1264); + temp2 = (step2[21] * CosPi1264) + (step2[26] * CosPi2064); step1[21] = (short)WrapLow(DctConstRoundShift(temp1)); step1[26] = (short)WrapLow(DctConstRoundShift(temp2)); - temp1 = -step2[22] * CosPi12_64 - step2[25] * CosPi20_64; - temp2 = -step2[22] * CosPi20_64 + step2[25] * CosPi12_64; + temp1 = (-step2[22] * CosPi1264) - (step2[25] * CosPi2064); + temp2 = (-step2[22] * CosPi2064) + (step2[25] * CosPi1264); step1[22] = (short)WrapLow(DctConstRoundShift(temp1)); step1[25] = (short)WrapLow(DctConstRoundShift(temp2)); step1[23] = step2[23]; @@ -1140,12 +1127,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp step1[28] = step2[28]; // stage 4 - temp1 = (step1[0] + step1[1]) * CosPi16_64; - temp2 = (step1[0] - step1[1]) * CosPi16_64; + temp1 = (step1[0] + step1[1]) * CosPi1664; + temp2 = (step1[0] - step1[1]) * CosPi1664; step2[0] = (short)WrapLow(DctConstRoundShift(temp1)); step2[1] = (short)WrapLow(DctConstRoundShift(temp2)); - temp1 = step1[2] * CosPi24_64 - step1[3] * CosPi8_64; - temp2 = step1[2] * CosPi8_64 + step1[3] * CosPi24_64; + temp1 = (step1[2] * CosPi2464) - (step1[3] * CosPi864); + temp2 = (step1[2] * CosPi864) + (step1[3] * CosPi2464); step2[2] = (short)WrapLow(DctConstRoundShift(temp1)); step2[3] = (short)WrapLow(DctConstRoundShift(temp2)); step2[4] = (short)WrapLow(step1[4] + step1[5]); @@ -1155,12 +1142,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp step2[8] = step1[8]; step2[15] = step1[15]; - temp1 = -step1[9] * CosPi8_64 + step1[14] * CosPi24_64; - temp2 = step1[9] * CosPi24_64 + step1[14] * CosPi8_64; + temp1 = (-step1[9] * CosPi864) + (step1[14] * CosPi2464); + temp2 = (step1[9] * CosPi2464) + (step1[14] * CosPi864); step2[9] = (short)WrapLow(DctConstRoundShift(temp1)); step2[14] = (short)WrapLow(DctConstRoundShift(temp2)); - temp1 = -step1[10] * CosPi24_64 - step1[13] * CosPi8_64; - temp2 = -step1[10] * CosPi8_64 + step1[13] * CosPi24_64; + temp1 = (-step1[10] * CosPi2464) - (step1[13] * CosPi864); + temp2 = (-step1[10] * CosPi864) + (step1[13] * CosPi2464); step2[10] = (short)WrapLow(DctConstRoundShift(temp1)); step2[13] = (short)WrapLow(DctConstRoundShift(temp2)); step2[11] = step1[11]; @@ -1190,8 +1177,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp step1[2] = (short)WrapLow(step2[1] - step2[2]); step1[3] = (short)WrapLow(step2[0] - step2[3]); step1[4] = step2[4]; - temp1 = (step2[6] - step2[5]) * CosPi16_64; - temp2 = (step2[5] + step2[6]) * CosPi16_64; + temp1 = (step2[6] - step2[5]) * CosPi1664; + temp2 = (step2[5] + step2[6]) * CosPi1664; step1[5] = (short)WrapLow(DctConstRoundShift(temp1)); step1[6] = (short)WrapLow(DctConstRoundShift(temp2)); step1[7] = step2[7]; @@ -1207,20 +1194,20 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp step1[16] = step2[16]; step1[17] = step2[17]; - temp1 = -step2[18] * CosPi8_64 + step2[29] * CosPi24_64; - temp2 = step2[18] * CosPi24_64 + step2[29] * CosPi8_64; + temp1 = (-step2[18] * CosPi864) + (step2[29] * CosPi2464); + temp2 = (step2[18] * CosPi2464) + (step2[29] * CosPi864); step1[18] = (short)WrapLow(DctConstRoundShift(temp1)); step1[29] = (short)WrapLow(DctConstRoundShift(temp2)); - temp1 = -step2[19] * CosPi8_64 + step2[28] * CosPi24_64; - temp2 = step2[19] * CosPi24_64 + step2[28] * CosPi8_64; + temp1 = (-step2[19] * CosPi864) + (step2[28] * CosPi2464); + temp2 = (step2[19] * CosPi2464) + (step2[28] * CosPi864); step1[19] = (short)WrapLow(DctConstRoundShift(temp1)); step1[28] = (short)WrapLow(DctConstRoundShift(temp2)); - temp1 = -step2[20] * CosPi24_64 - step2[27] * CosPi8_64; - temp2 = -step2[20] * CosPi8_64 + step2[27] * CosPi24_64; + temp1 = (-step2[20] * CosPi2464) - (step2[27] * CosPi864); + temp2 = (-step2[20] * CosPi864) + (step2[27] * CosPi2464); step1[20] = (short)WrapLow(DctConstRoundShift(temp1)); step1[27] = (short)WrapLow(DctConstRoundShift(temp2)); - temp1 = -step2[21] * CosPi24_64 - step2[26] * CosPi8_64; - temp2 = -step2[21] * CosPi8_64 + step2[26] * CosPi24_64; + temp1 = (-step2[21] * CosPi2464) - (step2[26] * CosPi864); + temp2 = (-step2[21] * CosPi864) + (step2[26] * CosPi2464); step1[21] = (short)WrapLow(DctConstRoundShift(temp1)); step1[26] = (short)WrapLow(DctConstRoundShift(temp2)); step1[22] = step2[22]; @@ -1241,12 +1228,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp step2[7] = (short)WrapLow(step1[0] - step1[7]); step2[8] = step1[8]; step2[9] = step1[9]; - temp1 = (-step1[10] + step1[13]) * CosPi16_64; - temp2 = (step1[10] + step1[13]) * CosPi16_64; + temp1 = (-step1[10] + step1[13]) * CosPi1664; + temp2 = (step1[10] + step1[13]) * CosPi1664; step2[10] = (short)WrapLow(DctConstRoundShift(temp1)); step2[13] = (short)WrapLow(DctConstRoundShift(temp2)); - temp1 = (-step1[11] + step1[12]) * CosPi16_64; - temp2 = (step1[11] + step1[12]) * CosPi16_64; + temp1 = (-step1[11] + step1[12]) * CosPi1664; + temp2 = (step1[11] + step1[12]) * CosPi1664; step2[11] = (short)WrapLow(DctConstRoundShift(temp1)); step2[12] = (short)WrapLow(DctConstRoundShift(temp2)); step2[14] = step1[14]; @@ -1292,20 +1279,20 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp step1[17] = step2[17]; step1[18] = step2[18]; step1[19] = step2[19]; - temp1 = (-step2[20] + step2[27]) * CosPi16_64; - temp2 = (step2[20] + step2[27]) * CosPi16_64; + temp1 = (-step2[20] + step2[27]) * CosPi1664; + temp2 = (step2[20] + step2[27]) * CosPi1664; step1[20] = (short)WrapLow(DctConstRoundShift(temp1)); step1[27] = (short)WrapLow(DctConstRoundShift(temp2)); - temp1 = (-step2[21] + step2[26]) * CosPi16_64; - temp2 = (step2[21] + step2[26]) * CosPi16_64; + temp1 = (-step2[21] + step2[26]) * CosPi1664; + temp2 = (step2[21] + step2[26]) * CosPi1664; step1[21] = (short)WrapLow(DctConstRoundShift(temp1)); step1[26] = (short)WrapLow(DctConstRoundShift(temp2)); - temp1 = (-step2[22] + step2[25]) * CosPi16_64; - temp2 = (step2[22] + step2[25]) * CosPi16_64; + temp1 = (-step2[22] + step2[25]) * CosPi1664; + temp2 = (step2[22] + step2[25]) * CosPi1664; step1[22] = (short)WrapLow(DctConstRoundShift(temp1)); step1[25] = (short)WrapLow(DctConstRoundShift(temp2)); - temp1 = (-step2[23] + step2[24]) * CosPi16_64; - temp2 = (step2[23] + step2[24]) * CosPi16_64; + temp1 = (-step2[23] + step2[24]) * CosPi1664; + temp2 = (step2[23] + step2[24]) * CosPi1664; step1[23] = (short)WrapLow(DctConstRoundShift(temp1)); step1[24] = (short)WrapLow(DctConstRoundShift(temp2)); step1[28] = step2[28]; @@ -1349,19 +1336,18 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp } [SkipLocalsInit] - public static void Idct32x321024Add(ReadOnlySpan input, Span dest, int stride) + public static void Idct32X321024Add(ReadOnlySpan input, Span dest, int stride) { - int i, j; Span output = stackalloc int[32 * 32]; Span outptr = output; Span tempIn = stackalloc int[32]; Span tempOut = stackalloc int[32]; // Rows - for (i = 0; i < 32; ++i) + for (int i = 0; i < 32; ++i) { short zeroCoeff = 0; - for (j = 0; j < 32; ++j) + for (int j = 0; j < 32; ++j) { zeroCoeff |= (short)input[j]; } @@ -1372,33 +1358,33 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp } else { - outptr[..32].Clear(); + outptr.Slice(0, 32).Clear(); } - input = input[32..]; - outptr = outptr[32..]; + input = input.Slice(32); + outptr = outptr.Slice(32); } // Columns - for (i = 0; i < 32; ++i) + for (int i = 0; i < 32; ++i) { - for (j = 0; j < 32; ++j) + for (int j = 0; j < 32; ++j) { - tempIn[j] = output[j * 32 + i]; + tempIn[j] = output[(j * 32) + i]; } Idct32(tempIn, tempOut); - for (j = 0; j < 32; ++j) + for (int j = 0; j < 32; ++j) { - dest[j * stride + i] = ClipPixelAdd(dest[j * stride + i], BitUtils.RoundPowerOfTwo(tempOut[j], 6)); + dest[(j * stride) + i] = + ClipPixelAdd(dest[(j * stride) + i], BitUtils.RoundPowerOfTwo(tempOut[j], 6)); } } } [SkipLocalsInit] - public static void Idct32x32135Add(ReadOnlySpan input, Span dest, int stride) + public static void Idct32X32135Add(ReadOnlySpan input, Span dest, int stride) { - int i, j; Span output = stackalloc int[32 * 32]; Span outptr = output; Span tempIn = stackalloc int[32]; @@ -1408,33 +1394,33 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp // Rows // Only upper-left 16x16 has non-zero coeff - for (i = 0; i < 16; ++i) + for (int i = 0; i < 16; ++i) { Idct32(input, outptr); - input = input[32..]; - outptr = outptr[32..]; + input = input.Slice(32); + outptr = outptr.Slice(32); } // Columns - for (i = 0; i < 32; ++i) + for (int i = 0; i < 32; ++i) { - for (j = 0; j < 32; ++j) + for (int j = 0; j < 32; ++j) { - tempIn[j] = output[j * 32 + i]; + tempIn[j] = output[(j * 32) + i]; } Idct32(tempIn, tempOut); - for (j = 0; j < 32; ++j) + for (int j = 0; j < 32; ++j) { - dest[j * stride + i] = ClipPixelAdd(dest[j * stride + i], BitUtils.RoundPowerOfTwo(tempOut[j], 6)); + dest[(j * stride) + i] = + ClipPixelAdd(dest[(j * stride) + i], BitUtils.RoundPowerOfTwo(tempOut[j], 6)); } } } [SkipLocalsInit] - public static void Idct32x3234Add(ReadOnlySpan input, Span dest, int stride) + public static void Idct32X3234Add(ReadOnlySpan input, Span dest, int stride) { - int i, j; Span output = stackalloc int[32 * 32]; Span outptr = output; Span tempIn = stackalloc int[32]; @@ -1444,61 +1430,61 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp // Rows // Only upper-left 8x8 has non-zero coeff - for (i = 0; i < 8; ++i) + for (int i = 0; i < 8; ++i) { Idct32(input, outptr); - input = input[32..]; - outptr = outptr[32..]; + input = input.Slice(32); + outptr = outptr.Slice(32); } // Columns - for (i = 0; i < 32; ++i) + for (int i = 0; i < 32; ++i) { - for (j = 0; j < 32; ++j) + for (int j = 0; j < 32; ++j) { - tempIn[j] = output[j * 32 + i]; + tempIn[j] = output[(j * 32) + i]; } Idct32(tempIn, tempOut); - for (j = 0; j < 32; ++j) + for (int j = 0; j < 32; ++j) { - dest[j * stride + i] = ClipPixelAdd(dest[j * stride + i], BitUtils.RoundPowerOfTwo(tempOut[j], 6)); + dest[(j * stride) + i] = + ClipPixelAdd(dest[(j * stride) + i], BitUtils.RoundPowerOfTwo(tempOut[j], 6)); } } } - public static void Idct32x321Add(ReadOnlySpan input, Span dest, int stride) + public static void Idct32X321Add(ReadOnlySpan input, Span dest, int stride) { - int i, j; long a1; - int output = WrapLow(DctConstRoundShift((short)input[0] * CosPi16_64)); + int output = WrapLow(DctConstRoundShift((short)input[0] * CosPi1664)); - output = WrapLow(DctConstRoundShift(output * CosPi16_64)); + output = WrapLow(DctConstRoundShift(output * CosPi1664)); a1 = BitUtils.RoundPowerOfTwo(output, 6); - for (j = 0; j < 32; ++j) + for (int j = 0; j < 32; ++j) { - for (i = 0; i < 32; ++i) + for (int i = 0; i < 32; ++i) { dest[i] = ClipPixelAdd(dest[i], a1); } - dest = dest[stride..]; + dest = dest.Slice(stride); } } [SkipLocalsInit] - public static void HighbdIwht4x416Add(ReadOnlySpan input, Span dest, int stride, int bd) + public static void HighbdIwht4X416Add(ReadOnlySpan input, Span dest, int stride, int bd) { /* 4-point reversible, orthonormal inverse Walsh-Hadamard in 3.5 adds, 0.5 shifts per pixel. */ - int i; + Span output = stackalloc int[16]; long a1, b1, c1, d1, e1; ReadOnlySpan ip = input; Span op = output; - for (i = 0; i < 4; i++) + for (int i = 0; i < 4; i++) { a1 = ip[0] >> UnitQuantShift; c1 = ip[1] >> UnitQuantShift; @@ -1515,12 +1501,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp op[1] = HighbdWrapLow(b1, bd); op[2] = HighbdWrapLow(c1, bd); op[3] = HighbdWrapLow(d1, bd); - ip = ip[4..]; - op = op[4..]; + ip = ip.Slice(4); + op = op.Slice(4); } ReadOnlySpan ip2 = output; - for (i = 0; i < 4; i++) + for (int i = 0; i < 4; i++) { a1 = ip2[4 * 0]; c1 = ip2[4 * 1]; @@ -1538,15 +1524,14 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp dest[stride * 2] = HighbdClipPixelAdd(dest[stride * 2], HighbdWrapLow(c1, bd), bd); dest[stride * 3] = HighbdClipPixelAdd(dest[stride * 3], HighbdWrapLow(d1, bd), bd); - ip2 = ip2[1..]; - dest = dest[1..]; + ip2 = ip2.Slice(1); + dest = dest.Slice(1); } } [SkipLocalsInit] - public static void HighbdIwht4x41Add(ReadOnlySpan input, Span dest, int stride, int bd) + public static void HighbdIwht4X41Add(ReadOnlySpan input, Span dest, int stride, int bd) { - int i; long a1, e1; Span tmp = stackalloc int[4]; ReadOnlySpan ip = input; @@ -1559,7 +1544,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp op[1] = op[2] = op[3] = HighbdWrapLow(e1, bd); ReadOnlySpan ip2 = tmp; - for (i = 0; i < 4; i++) + for (int i = 0; i < 4; i++) { e1 = ip2[0] >> 1; a1 = ip2[0] - e1; @@ -1567,8 +1552,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp dest[stride * 1] = HighbdClipPixelAdd(dest[stride * 1], e1, bd); dest[stride * 2] = HighbdClipPixelAdd(dest[stride * 2], e1, bd); dest[stride * 3] = HighbdClipPixelAdd(dest[stride * 3], e1, bd); - ip2 = ip2[1..]; - dest = dest[1..]; + ip2 = ip2.Slice(1); + dest = dest.Slice(1); } } @@ -1583,31 +1568,29 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp if (DetectInvalidHighbdInput(input, 4) != 0) { Debug.Assert(false, "invalid highbd txfm input"); - output[..4].Clear(); - + output.Slice(0, 4).Clear(); return; } if ((x0 | x1 | x2 | x3) == 0) { - output[..4].Clear(); - + output.Slice(0, 4).Clear(); return; } - s0 = (long)SinPi1_9 * x0; - s1 = (long)SinPi2_9 * x0; - s2 = (long)SinPi3_9 * x1; - s3 = (long)SinPi4_9 * x2; - s4 = (long)SinPi1_9 * x2; - s5 = (long)SinPi2_9 * x3; - s6 = (long)SinPi4_9 * x3; + s0 = (long)SinPi19 * x0; + s1 = (long)SinPi29 * x0; + s2 = (long)SinPi39 * x1; + s3 = (long)SinPi49 * x2; + s4 = (long)SinPi19 * x2; + s5 = (long)SinPi29 * x3; + s6 = (long)SinPi49 * x3; s7 = HighbdWrapLow(x0 - x2 + x3, bd); s0 = s0 + s3 + s5; s1 = s1 - s4 - s6; s3 = s2; - s2 = SinPi3_9 * s7; + s2 = SinPi39 * s7; // 1-D transform scaling factor is sqrt(2). // The overall dynamic range is 14b (input) + 14b (multiplication scaling) @@ -1628,18 +1611,17 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp if (DetectInvalidHighbdInput(input, 4) != 0) { Debug.Assert(false, "invalid highbd txfm input"); - output[..4].Clear(); - + output.Slice(0, 4).Clear(); return; } // stage 1 - temp1 = (input[0] + input[2]) * (long)CosPi16_64; - temp2 = (input[0] - input[2]) * (long)CosPi16_64; + temp1 = (input[0] + input[2]) * (long)CosPi1664; + temp2 = (input[0] - input[2]) * (long)CosPi1664; step[0] = HighbdWrapLow(DctConstRoundShift(temp1), bd); step[1] = HighbdWrapLow(DctConstRoundShift(temp2), bd); - temp1 = input[1] * (long)CosPi24_64 - input[3] * (long)CosPi8_64; - temp2 = input[1] * (long)CosPi8_64 + input[3] * (long)CosPi24_64; + temp1 = (input[1] * (long)CosPi2464) - (input[3] * (long)CosPi864); + temp2 = (input[1] * (long)CosPi864) + (input[3] * (long)CosPi2464); step[2] = HighbdWrapLow(DctConstRoundShift(temp1), bd); step[3] = HighbdWrapLow(DctConstRoundShift(temp2), bd); @@ -1651,54 +1633,53 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp } [SkipLocalsInit] - public static void HighbdIdct4x416Add(ReadOnlySpan input, Span dest, int stride, int bd) + public static void HighbdIdct4X416Add(ReadOnlySpan input, Span dest, int stride, int bd) { - int i, j; Span output = stackalloc int[4 * 4]; Span outptr = output; Span tempIn = stackalloc int[4]; Span tempOut = stackalloc int[4]; // Rows - for (i = 0; i < 4; ++i) + for (int i = 0; i < 4; ++i) { HighbdIdct4(input, outptr, bd); - input = input[4..]; - outptr = outptr[4..]; + input = input.Slice(4); + outptr = outptr.Slice(4); } // Columns - for (i = 0; i < 4; ++i) + for (int i = 0; i < 4; ++i) { - for (j = 0; j < 4; ++j) + for (int j = 0; j < 4; ++j) { - tempIn[j] = output[j * 4 + i]; + tempIn[j] = output[(j * 4) + i]; } HighbdIdct4(tempIn, tempOut, bd); - for (j = 0; j < 4; ++j) + for (int j = 0; j < 4; ++j) { - dest[j * stride + i] = HighbdClipPixelAdd(dest[j * stride + i], BitUtils.RoundPowerOfTwo(tempOut[j], 4), bd); + dest[(j * stride) + i] = HighbdClipPixelAdd(dest[(j * stride) + i], + BitUtils.RoundPowerOfTwo(tempOut[j], 4), bd); } } } - public static void HighbdIdct4x41Add(ReadOnlySpan input, Span dest, int stride, int bd) + public static void HighbdIdct4X41Add(ReadOnlySpan input, Span dest, int stride, int bd) { - int i; long a1; - int output = HighbdWrapLow(DctConstRoundShift(input[0] * (long)CosPi16_64), bd); + int output = HighbdWrapLow(DctConstRoundShift(input[0] * (long)CosPi1664), bd); - output = HighbdWrapLow(DctConstRoundShift(output * (long)CosPi16_64), bd); + output = HighbdWrapLow(DctConstRoundShift(output * (long)CosPi1664), bd); a1 = BitUtils.RoundPowerOfTwo(output, 4); - for (i = 0; i < 4; i++) + for (int i = 0; i < 4; i++) { dest[0] = HighbdClipPixelAdd(dest[0], a1, bd); dest[1] = HighbdClipPixelAdd(dest[1], a1, bd); dest[2] = HighbdClipPixelAdd(dest[2], a1, bd); dest[3] = HighbdClipPixelAdd(dest[3], a1, bd); - dest = dest[stride..]; + dest = dest.Slice(stride); } } @@ -1717,27 +1698,25 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp if (DetectInvalidHighbdInput(input, 8) != 0) { Debug.Assert(false, "invalid highbd txfm input"); - output[..8].Clear(); - + output.Slice(0, 8).Clear(); return; } if ((x0 | x1 | x2 | x3 | x4 | x5 | x6 | x7) == 0) { - output[..8].Clear(); - + output.Slice(0, 8).Clear(); return; } // stage 1 - s0 = (long)CosPi2_64 * x0 + (long)CosPi30_64 * x1; - s1 = (long)CosPi30_64 * x0 - (long)CosPi2_64 * x1; - s2 = (long)CosPi10_64 * x2 + (long)CosPi22_64 * x3; - s3 = (long)CosPi22_64 * x2 - (long)CosPi10_64 * x3; - s4 = (long)CosPi18_64 * x4 + (long)CosPi14_64 * x5; - s5 = (long)CosPi14_64 * x4 - (long)CosPi18_64 * x5; - s6 = (long)CosPi26_64 * x6 + (long)CosPi6_64 * x7; - s7 = (long)CosPi6_64 * x6 - (long)CosPi26_64 * x7; + s0 = ((long)CosPi264 * x0) + ((long)CosPi3064 * x1); + s1 = ((long)CosPi3064 * x0) - ((long)CosPi264 * x1); + s2 = ((long)CosPi1064 * x2) + ((long)CosPi2264 * x3); + s3 = ((long)CosPi2264 * x2) - ((long)CosPi1064 * x3); + s4 = ((long)CosPi1864 * x4) + ((long)CosPi1464 * x5); + s5 = ((long)CosPi1464 * x4) - ((long)CosPi1864 * x5); + s6 = ((long)CosPi2664 * x6) + ((long)CosPi664 * x7); + s7 = ((long)CosPi664 * x6) - ((long)CosPi2664 * x7); x0 = HighbdWrapLow(DctConstRoundShift(s0 + s4), bd); x1 = HighbdWrapLow(DctConstRoundShift(s1 + s5), bd); @@ -1753,10 +1732,10 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp s1 = x1; s2 = x2; s3 = x3; - s4 = (long)CosPi8_64 * x4 + (long)CosPi24_64 * x5; - s5 = (long)CosPi24_64 * x4 - (long)CosPi8_64 * x5; - s6 = (long)(-CosPi24_64) * x6 + (long)CosPi8_64 * x7; - s7 = (long)CosPi8_64 * x6 + (long)CosPi24_64 * x7; + s4 = ((long)CosPi864 * x4) + ((long)CosPi2464 * x5); + s5 = ((long)CosPi2464 * x4) - ((long)CosPi864 * x5); + s6 = ((long)-CosPi2464 * x6) + ((long)CosPi864 * x7); + s7 = ((long)CosPi864 * x6) + ((long)CosPi2464 * x7); x0 = HighbdWrapLow(s0 + s2, bd); x1 = HighbdWrapLow(s1 + s3, bd); @@ -1768,10 +1747,10 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp x7 = HighbdWrapLow(DctConstRoundShift(s5 - s7), bd); // stage 3 - s2 = (long)CosPi16_64 * (x2 + x3); - s3 = (long)CosPi16_64 * (x2 - x3); - s6 = (long)CosPi16_64 * (x6 + x7); - s7 = (long)CosPi16_64 * (x6 - x7); + s2 = (long)CosPi1664 * (x2 + x3); + s3 = (long)CosPi1664 * (x2 - x3); + s6 = (long)CosPi1664 * (x6 + x7); + s7 = (long)CosPi1664 * (x6 - x7); x2 = HighbdWrapLow(DctConstRoundShift(s2), bd); x3 = HighbdWrapLow(DctConstRoundShift(s3), bd); @@ -1798,8 +1777,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp if (DetectInvalidHighbdInput(input, 8) != 0) { Debug.Assert(false, "invalid highbd txfm input"); - output[..8].Clear(); - + output.Slice(0, 8).Clear(); return; } @@ -1808,12 +1786,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp step1[2] = input[4]; step1[1] = input[2]; step1[3] = input[6]; - temp1 = input[1] * (long)CosPi28_64 - input[7] * (long)CosPi4_64; - temp2 = input[1] * (long)CosPi4_64 + input[7] * (long)CosPi28_64; + temp1 = (input[1] * (long)CosPi2864) - (input[7] * (long)CosPi464); + temp2 = (input[1] * (long)CosPi464) + (input[7] * (long)CosPi2864); step1[4] = HighbdWrapLow(DctConstRoundShift(temp1), bd); step1[7] = HighbdWrapLow(DctConstRoundShift(temp2), bd); - temp1 = input[5] * (long)CosPi12_64 - input[3] * (long)CosPi20_64; - temp2 = input[5] * (long)CosPi20_64 + input[3] * (long)CosPi12_64; + temp1 = (input[5] * (long)CosPi1264) - (input[3] * (long)CosPi2064); + temp2 = (input[5] * (long)CosPi2064) + (input[3] * (long)CosPi1264); step1[5] = HighbdWrapLow(DctConstRoundShift(temp1), bd); step1[6] = HighbdWrapLow(DctConstRoundShift(temp2), bd); @@ -1828,8 +1806,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp // stage 3 - odd half step1[4] = step2[4]; - temp1 = (step2[6] - step2[5]) * (long)CosPi16_64; - temp2 = (step2[5] + step2[6]) * (long)CosPi16_64; + temp1 = (step2[6] - step2[5]) * (long)CosPi1664; + temp2 = (step2[5] + step2[6]) * (long)CosPi1664; step1[5] = HighbdWrapLow(DctConstRoundShift(temp1), bd); step1[6] = HighbdWrapLow(DctConstRoundShift(temp2), bd); step1[7] = step2[7]; @@ -1846,42 +1824,41 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp } [SkipLocalsInit] - public static void HighbdIdct8x864Add(ReadOnlySpan input, Span dest, int stride, int bd) + public static void HighbdIdct8X864Add(ReadOnlySpan input, Span dest, int stride, int bd) { - int i, j; Span output = stackalloc int[8 * 8]; Span outptr = output; Span tempIn = stackalloc int[8]; Span tempOut = stackalloc int[8]; // First transform rows - for (i = 0; i < 8; ++i) + for (int i = 0; i < 8; ++i) { HighbdIdct8(input, outptr, bd); - input = input[8..]; - outptr = outptr[8..]; + input = input.Slice(8); + outptr = outptr.Slice(8); } // Then transform columns - for (i = 0; i < 8; ++i) + for (int i = 0; i < 8; ++i) { - for (j = 0; j < 8; ++j) + for (int j = 0; j < 8; ++j) { - tempIn[j] = output[j * 8 + i]; + tempIn[j] = output[(j * 8) + i]; } HighbdIdct8(tempIn, tempOut, bd); - for (j = 0; j < 8; ++j) + for (int j = 0; j < 8; ++j) { - dest[j * stride + i] = HighbdClipPixelAdd(dest[j * stride + i], BitUtils.RoundPowerOfTwo(tempOut[j], 5), bd); + dest[(j * stride) + i] = HighbdClipPixelAdd(dest[(j * stride) + i], + BitUtils.RoundPowerOfTwo(tempOut[j], 5), bd); } } } [SkipLocalsInit] - public static void HighbdIdct8x812Add(ReadOnlySpan input, Span dest, int stride, int bd) + public static void HighbdIdct8X812Add(ReadOnlySpan input, Span dest, int stride, int bd) { - int i, j; Span output = stackalloc int[8 * 8]; Span outptr = output; Span tempIn = stackalloc int[8]; @@ -1891,45 +1868,45 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp // First transform rows // Only first 4 row has non-zero coefs - for (i = 0; i < 4; ++i) + for (int i = 0; i < 4; ++i) { HighbdIdct8(input, outptr, bd); - input = input[8..]; - outptr = outptr[8..]; + input = input.Slice(8); + outptr = outptr.Slice(8); } // Then transform columns - for (i = 0; i < 8; ++i) + for (int i = 0; i < 8; ++i) { - for (j = 0; j < 8; ++j) + for (int j = 0; j < 8; ++j) { - tempIn[j] = output[j * 8 + i]; + tempIn[j] = output[(j * 8) + i]; } HighbdIdct8(tempIn, tempOut, bd); - for (j = 0; j < 8; ++j) + for (int j = 0; j < 8; ++j) { - dest[j * stride + i] = HighbdClipPixelAdd(dest[j * stride + i], BitUtils.RoundPowerOfTwo(tempOut[j], 5), bd); + dest[(j * stride) + i] = HighbdClipPixelAdd(dest[(j * stride) + i], + BitUtils.RoundPowerOfTwo(tempOut[j], 5), bd); } } } - public static void Vpx_Highbdidct8x8_1_add_c(ReadOnlySpan input, Span dest, int stride, int bd) + public static void VpxHighbdidct8X81AddC(ReadOnlySpan input, Span dest, int stride, int bd) { - int i, j; long a1; - int output = HighbdWrapLow(DctConstRoundShift(input[0] * (long)CosPi16_64), bd); + int output = HighbdWrapLow(DctConstRoundShift(input[0] * (long)CosPi1664), bd); - output = HighbdWrapLow(DctConstRoundShift(output * (long)CosPi16_64), bd); + output = HighbdWrapLow(DctConstRoundShift(output * (long)CosPi1664), bd); a1 = BitUtils.RoundPowerOfTwo(output, 5); - for (j = 0; j < 8; ++j) + for (int j = 0; j < 8; ++j) { - for (i = 0; i < 8; ++i) + for (int i = 0; i < 8; ++i) { dest[i] = HighbdClipPixelAdd(dest[i], a1, bd); } - dest = dest[stride..]; + dest = dest.Slice(stride); } } @@ -1953,39 +1930,36 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp int x13 = input[12]; int x14 = input[1]; int x15 = input[14]; - if (DetectInvalidHighbdInput(input, 16) != 0) { Debug.Assert(false, "invalid highbd txfm input"); - output[..16].Clear(); - + output.Slice(0, 16).Clear(); return; } if ((x0 | x1 | x2 | x3 | x4 | x5 | x6 | x7 | x8 | x9 | x10 | x11 | x12 | x13 | x14 | x15) == 0) { - output[..16].Clear(); - + output.Slice(0, 16).Clear(); return; } // stage 1 - s0 = x0 * (long)CosPi1_64 + x1 * (long)CosPi31_64; - s1 = x0 * (long)CosPi31_64 - x1 * (long)CosPi1_64; - s2 = x2 * (long)CosPi5_64 + x3 * (long)CosPi27_64; - s3 = x2 * (long)CosPi27_64 - x3 * (long)CosPi5_64; - s4 = x4 * (long)CosPi9_64 + x5 * (long)CosPi23_64; - s5 = x4 * (long)CosPi23_64 - x5 * (long)CosPi9_64; - s6 = x6 * (long)CosPi13_64 + x7 * (long)CosPi19_64; - s7 = x6 * (long)CosPi19_64 - x7 * (long)CosPi13_64; - s8 = x8 * (long)CosPi17_64 + x9 * (long)CosPi15_64; - s9 = x8 * (long)CosPi15_64 - x9 * (long)CosPi17_64; - s10 = x10 * (long)CosPi21_64 + x11 * (long)CosPi11_64; - s11 = x10 * (long)CosPi11_64 - x11 * (long)CosPi21_64; - s12 = x12 * (long)CosPi25_64 + x13 * (long)CosPi7_64; - s13 = x12 * (long)CosPi7_64 - x13 * (long)CosPi25_64; - s14 = x14 * (long)CosPi29_64 + x15 * (long)CosPi3_64; - s15 = x14 * (long)CosPi3_64 - x15 * (long)CosPi29_64; + s0 = (x0 * (long)CosPi164) + (x1 * (long)CosPi3164); + s1 = (x0 * (long)CosPi3164) - (x1 * (long)CosPi164); + s2 = (x2 * (long)CosPi564) + (x3 * (long)CosPi2764); + s3 = (x2 * (long)CosPi2764) - (x3 * (long)CosPi564); + s4 = (x4 * (long)CosPi964) + (x5 * (long)CosPi2364); + s5 = (x4 * (long)CosPi2364) - (x5 * (long)CosPi964); + s6 = (x6 * (long)CosPi1364) + (x7 * (long)CosPi1964); + s7 = (x6 * (long)CosPi1964) - (x7 * (long)CosPi1364); + s8 = (x8 * (long)CosPi1764) + (x9 * (long)CosPi1564); + s9 = (x8 * (long)CosPi1564) - (x9 * (long)CosPi1764); + s10 = (x10 * (long)CosPi2164) + (x11 * (long)CosPi1164); + s11 = (x10 * (long)CosPi1164) - (x11 * (long)CosPi2164); + s12 = (x12 * (long)CosPi2564) + (x13 * (long)CosPi764); + s13 = (x12 * (long)CosPi764) - (x13 * (long)CosPi2564); + s14 = (x14 * (long)CosPi2964) + (x15 * (long)CosPi364); + s15 = (x14 * (long)CosPi364) - (x15 * (long)CosPi2964); x0 = HighbdWrapLow(DctConstRoundShift(s0 + s8), bd); x1 = HighbdWrapLow(DctConstRoundShift(s1 + s9), bd); @@ -2013,14 +1987,14 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp s5 = x5; s6 = x6; s7 = x7; - s8 = x8 * (long)CosPi4_64 + x9 * (long)CosPi28_64; - s9 = x8 * (long)CosPi28_64 - x9 * (long)CosPi4_64; - s10 = x10 * (long)CosPi20_64 + x11 * (long)CosPi12_64; - s11 = x10 * (long)CosPi12_64 - x11 * (long)CosPi20_64; - s12 = -x12 * (long)CosPi28_64 + x13 * (long)CosPi4_64; - s13 = x12 * (long)CosPi4_64 + x13 * (long)CosPi28_64; - s14 = -x14 * (long)CosPi12_64 + x15 * (long)CosPi20_64; - s15 = x14 * (long)CosPi20_64 + x15 * (long)CosPi12_64; + s8 = (x8 * (long)CosPi464) + (x9 * (long)CosPi2864); + s9 = (x8 * (long)CosPi2864) - (x9 * (long)CosPi464); + s10 = (x10 * (long)CosPi2064) + (x11 * (long)CosPi1264); + s11 = (x10 * (long)CosPi1264) - (x11 * (long)CosPi2064); + s12 = (-x12 * (long)CosPi2864) + (x13 * (long)CosPi464); + s13 = (x12 * (long)CosPi464) + (x13 * (long)CosPi2864); + s14 = (-x14 * (long)CosPi1264) + (x15 * (long)CosPi2064); + s15 = (x14 * (long)CosPi2064) + (x15 * (long)CosPi1264); x0 = HighbdWrapLow(s0 + s4, bd); x1 = HighbdWrapLow(s1 + s5, bd); @@ -2044,18 +2018,18 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp s1 = x1; s2 = x2; s3 = x3; - s4 = x4 * (long)CosPi8_64 + x5 * (long)CosPi24_64; - s5 = x4 * (long)CosPi24_64 - x5 * (long)CosPi8_64; - s6 = -x6 * (long)CosPi24_64 + x7 * (long)CosPi8_64; - s7 = x6 * (long)CosPi8_64 + x7 * (long)CosPi24_64; + s4 = (x4 * (long)CosPi864) + (x5 * (long)CosPi2464); + s5 = (x4 * (long)CosPi2464) - (x5 * (long)CosPi864); + s6 = (-x6 * (long)CosPi2464) + (x7 * (long)CosPi864); + s7 = (x6 * (long)CosPi864) + (x7 * (long)CosPi2464); s8 = x8; s9 = x9; s10 = x10; s11 = x11; - s12 = x12 * (long)CosPi8_64 + x13 * (long)CosPi24_64; - s13 = x12 * (long)CosPi24_64 - x13 * (long)CosPi8_64; - s14 = -x14 * (long)CosPi24_64 + x15 * (long)CosPi8_64; - s15 = x14 * (long)CosPi8_64 + x15 * (long)CosPi24_64; + s12 = (x12 * (long)CosPi864) + (x13 * (long)CosPi2464); + s13 = (x12 * (long)CosPi2464) - (x13 * (long)CosPi864); + s14 = (-x14 * (long)CosPi2464) + (x15 * (long)CosPi864); + s15 = (x14 * (long)CosPi864) + (x15 * (long)CosPi2464); x0 = HighbdWrapLow(s0 + s2, bd); x1 = HighbdWrapLow(s1 + s3, bd); @@ -2075,14 +2049,14 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp x15 = HighbdWrapLow(DctConstRoundShift(s13 - s15), bd); // stage 4 - s2 = (long)(-CosPi16_64) * (x2 + x3); - s3 = (long)CosPi16_64 * (x2 - x3); - s6 = (long)CosPi16_64 * (x6 + x7); - s7 = (long)CosPi16_64 * (-x6 + x7); - s10 = (long)CosPi16_64 * (x10 + x11); - s11 = (long)CosPi16_64 * (-x10 + x11); - s14 = (long)(-CosPi16_64) * (x14 + x15); - s15 = (long)CosPi16_64 * (x14 - x15); + s2 = (long)-CosPi1664 * (x2 + x3); + s3 = (long)CosPi1664 * (x2 - x3); + s6 = (long)CosPi1664 * (x6 + x7); + s7 = (long)CosPi1664 * (-x6 + x7); + s10 = (long)CosPi1664 * (x10 + x11); + s11 = (long)CosPi1664 * (-x10 + x11); + s14 = (long)-CosPi1664 * (x14 + x15); + s15 = (long)CosPi1664 * (x14 - x15); x2 = HighbdWrapLow(DctConstRoundShift(s2), bd); x3 = HighbdWrapLow(DctConstRoundShift(s3), bd); @@ -2121,8 +2095,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp if (DetectInvalidHighbdInput(input, 16) != 0) { Debug.Assert(false, "invalid highbd txfm input"); - output[..16].Clear(); - + output.Slice(0, 16).Clear(); return; } @@ -2154,23 +2127,23 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp step2[6] = step1[6]; step2[7] = step1[7]; - temp1 = step1[8] * (long)CosPi30_64 - step1[15] * (long)CosPi2_64; - temp2 = step1[8] * (long)CosPi2_64 + step1[15] * (long)CosPi30_64; + temp1 = (step1[8] * (long)CosPi3064) - (step1[15] * (long)CosPi264); + temp2 = (step1[8] * (long)CosPi264) + (step1[15] * (long)CosPi3064); step2[8] = HighbdWrapLow(DctConstRoundShift(temp1), bd); step2[15] = HighbdWrapLow(DctConstRoundShift(temp2), bd); - temp1 = step1[9] * (long)CosPi14_64 - step1[14] * (long)CosPi18_64; - temp2 = step1[9] * (long)CosPi18_64 + step1[14] * (long)CosPi14_64; + temp1 = (step1[9] * (long)CosPi1464) - (step1[14] * (long)CosPi1864); + temp2 = (step1[9] * (long)CosPi1864) + (step1[14] * (long)CosPi1464); step2[9] = HighbdWrapLow(DctConstRoundShift(temp1), bd); step2[14] = HighbdWrapLow(DctConstRoundShift(temp2), bd); - temp1 = step1[10] * (long)CosPi22_64 - step1[13] * (long)CosPi10_64; - temp2 = step1[10] * (long)CosPi10_64 + step1[13] * (long)CosPi22_64; + temp1 = (step1[10] * (long)CosPi2264) - (step1[13] * (long)CosPi1064); + temp2 = (step1[10] * (long)CosPi1064) + (step1[13] * (long)CosPi2264); step2[10] = HighbdWrapLow(DctConstRoundShift(temp1), bd); step2[13] = HighbdWrapLow(DctConstRoundShift(temp2), bd); - temp1 = step1[11] * (long)CosPi6_64 - step1[12] * (long)CosPi26_64; - temp2 = step1[11] * (long)CosPi26_64 + step1[12] * (long)CosPi6_64; + temp1 = (step1[11] * (long)CosPi664) - (step1[12] * (long)CosPi2664); + temp2 = (step1[11] * (long)CosPi2664) + (step1[12] * (long)CosPi664); step2[11] = HighbdWrapLow(DctConstRoundShift(temp1), bd); step2[12] = HighbdWrapLow(DctConstRoundShift(temp2), bd); @@ -2180,12 +2153,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp step1[2] = step2[2]; step1[3] = step2[3]; - temp1 = step2[4] * (long)CosPi28_64 - step2[7] * (long)CosPi4_64; - temp2 = step2[4] * (long)CosPi4_64 + step2[7] * (long)CosPi28_64; + temp1 = (step2[4] * (long)CosPi2864) - (step2[7] * (long)CosPi464); + temp2 = (step2[4] * (long)CosPi464) + (step2[7] * (long)CosPi2864); step1[4] = HighbdWrapLow(DctConstRoundShift(temp1), bd); step1[7] = HighbdWrapLow(DctConstRoundShift(temp2), bd); - temp1 = step2[5] * (long)CosPi12_64 - step2[6] * (long)CosPi20_64; - temp2 = step2[5] * (long)CosPi20_64 + step2[6] * (long)CosPi12_64; + temp1 = (step2[5] * (long)CosPi1264) - (step2[6] * (long)CosPi2064); + temp2 = (step2[5] * (long)CosPi2064) + (step2[6] * (long)CosPi1264); step1[5] = HighbdWrapLow(DctConstRoundShift(temp1), bd); step1[6] = HighbdWrapLow(DctConstRoundShift(temp2), bd); @@ -2199,12 +2172,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp step1[15] = HighbdWrapLow(step2[14] + step2[15], bd); // stage 4 - temp1 = (step1[0] + step1[1]) * (long)CosPi16_64; - temp2 = (step1[0] - step1[1]) * (long)CosPi16_64; + temp1 = (step1[0] + step1[1]) * (long)CosPi1664; + temp2 = (step1[0] - step1[1]) * (long)CosPi1664; step2[0] = HighbdWrapLow(DctConstRoundShift(temp1), bd); step2[1] = HighbdWrapLow(DctConstRoundShift(temp2), bd); - temp1 = step1[2] * (long)CosPi24_64 - step1[3] * (long)CosPi8_64; - temp2 = step1[2] * (long)CosPi8_64 + step1[3] * (long)CosPi24_64; + temp1 = (step1[2] * (long)CosPi2464) - (step1[3] * (long)CosPi864); + temp2 = (step1[2] * (long)CosPi864) + (step1[3] * (long)CosPi2464); step2[2] = HighbdWrapLow(DctConstRoundShift(temp1), bd); step2[3] = HighbdWrapLow(DctConstRoundShift(temp2), bd); step2[4] = HighbdWrapLow(step1[4] + step1[5], bd); @@ -2214,12 +2187,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp step2[8] = step1[8]; step2[15] = step1[15]; - temp1 = -step1[9] * (long)CosPi8_64 + step1[14] * (long)CosPi24_64; - temp2 = step1[9] * (long)CosPi24_64 + step1[14] * (long)CosPi8_64; + temp1 = (-step1[9] * (long)CosPi864) + (step1[14] * (long)CosPi2464); + temp2 = (step1[9] * (long)CosPi2464) + (step1[14] * (long)CosPi864); step2[9] = HighbdWrapLow(DctConstRoundShift(temp1), bd); step2[14] = HighbdWrapLow(DctConstRoundShift(temp2), bd); - temp1 = -step1[10] * (long)CosPi24_64 - step1[13] * (long)CosPi8_64; - temp2 = -step1[10] * (long)CosPi8_64 + step1[13] * (long)CosPi24_64; + temp1 = (-step1[10] * (long)CosPi2464) - (step1[13] * (long)CosPi864); + temp2 = (-step1[10] * (long)CosPi864) + (step1[13] * (long)CosPi2464); step2[10] = HighbdWrapLow(DctConstRoundShift(temp1), bd); step2[13] = HighbdWrapLow(DctConstRoundShift(temp2), bd); step2[11] = step1[11]; @@ -2231,8 +2204,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp step1[2] = HighbdWrapLow(step2[1] - step2[2], bd); step1[3] = HighbdWrapLow(step2[0] - step2[3], bd); step1[4] = step2[4]; - temp1 = (step2[6] - step2[5]) * (long)CosPi16_64; - temp2 = (step2[5] + step2[6]) * (long)CosPi16_64; + temp1 = (step2[6] - step2[5]) * (long)CosPi1664; + temp2 = (step2[5] + step2[6]) * (long)CosPi1664; step1[5] = HighbdWrapLow(DctConstRoundShift(temp1), bd); step1[6] = HighbdWrapLow(DctConstRoundShift(temp2), bd); step1[7] = step2[7]; @@ -2257,12 +2230,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp step2[7] = HighbdWrapLow(step1[0] - step1[7], bd); step2[8] = step1[8]; step2[9] = step1[9]; - temp1 = (-step1[10] + step1[13]) * (long)CosPi16_64; - temp2 = (step1[10] + step1[13]) * (long)CosPi16_64; + temp1 = (-step1[10] + step1[13]) * (long)CosPi1664; + temp2 = (step1[10] + step1[13]) * (long)CosPi1664; step2[10] = HighbdWrapLow(DctConstRoundShift(temp1), bd); step2[13] = HighbdWrapLow(DctConstRoundShift(temp2), bd); - temp1 = (-step1[11] + step1[12]) * (long)CosPi16_64; - temp2 = (step1[11] + step1[12]) * (long)CosPi16_64; + temp1 = (-step1[11] + step1[12]) * (long)CosPi1664; + temp2 = (step1[11] + step1[12]) * (long)CosPi1664; step2[11] = HighbdWrapLow(DctConstRoundShift(temp1), bd); step2[12] = HighbdWrapLow(DctConstRoundShift(temp2), bd); step2[14] = step1[14]; @@ -2288,42 +2261,41 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp } [SkipLocalsInit] - public static void HighbdIdct16x16256Add(ReadOnlySpan input, Span dest, int stride, int bd) + public static void HighbdIdct16X16256Add(ReadOnlySpan input, Span dest, int stride, int bd) { - int i, j; Span output = stackalloc int[16 * 16]; Span outptr = output; Span tempIn = stackalloc int[16]; Span tempOut = stackalloc int[16]; // First transform rows - for (i = 0; i < 16; ++i) + for (int i = 0; i < 16; ++i) { HighbdIdct16(input, outptr, bd); - input = input[16..]; - outptr = outptr[16..]; + input = input.Slice(16); + outptr = outptr.Slice(16); } // Then transform columns - for (i = 0; i < 16; ++i) + for (int i = 0; i < 16; ++i) { - for (j = 0; j < 16; ++j) + for (int j = 0; j < 16; ++j) { - tempIn[j] = output[j * 16 + i]; + tempIn[j] = output[(j * 16) + i]; } HighbdIdct16(tempIn, tempOut, bd); - for (j = 0; j < 16; ++j) + for (int j = 0; j < 16; ++j) { - dest[j * stride + i] = HighbdClipPixelAdd(dest[j * stride + i], BitUtils.RoundPowerOfTwo(tempOut[j], 6), bd); + dest[(j * stride) + i] = HighbdClipPixelAdd(dest[(j * stride) + i], + BitUtils.RoundPowerOfTwo(tempOut[j], 6), bd); } } } [SkipLocalsInit] - public static void HighbdIdct16x1638Add(ReadOnlySpan input, Span dest, int stride, int bd) + public static void HighbdIdct16X1638Add(ReadOnlySpan input, Span dest, int stride, int bd) { - int i, j; Span output = stackalloc int[16 * 16]; Span outptr = output; Span tempIn = stackalloc int[16]; @@ -2333,35 +2305,34 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp // First transform rows. Since all non-zero dct coefficients are in // upper-left 8x8 area, we only need to calculate first 8 rows here. - for (i = 0; i < 8; ++i) + for (int i = 0; i < 8; ++i) { HighbdIdct16(input, outptr, bd); - input = input[16..]; - outptr = outptr[16..]; + input = input.Slice(16); + outptr = outptr.Slice(16); } // Then transform columns - for (i = 0; i < 16; ++i) + for (int i = 0; i < 16; ++i) { Span destT = dest; - for (j = 0; j < 16; ++j) + for (int j = 0; j < 16; ++j) { - tempIn[j] = output[j * 16 + i]; + tempIn[j] = output[(j * 16) + i]; } HighbdIdct16(tempIn, tempOut, bd); - for (j = 0; j < 16; ++j) + for (int j = 0; j < 16; ++j) { destT[i] = HighbdClipPixelAdd(destT[i], BitUtils.RoundPowerOfTwo(tempOut[j], 6), bd); - destT = destT[stride..]; + destT = destT.Slice(stride); } } } [SkipLocalsInit] - public static void HighbdIdct16x1610Add(ReadOnlySpan input, Span dest, int stride, int bd) + public static void HighbdIdct16X1610Add(ReadOnlySpan input, Span dest, int stride, int bd) { - int i, j; Span output = stackalloc int[16 * 16]; Span outptr = output; Span tempIn = stackalloc int[16]; @@ -2371,45 +2342,45 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp // First transform rows. Since all non-zero dct coefficients are in // upper-left 4x4 area, we only need to calculate first 4 rows here. - for (i = 0; i < 4; ++i) + for (int i = 0; i < 4; ++i) { HighbdIdct16(input, outptr, bd); - input = input[16..]; - outptr = outptr[16..]; + input = input.Slice(16); + outptr = outptr.Slice(16); } // Then transform columns - for (i = 0; i < 16; ++i) + for (int i = 0; i < 16; ++i) { - for (j = 0; j < 16; ++j) + for (int j = 0; j < 16; ++j) { - tempIn[j] = output[j * 16 + i]; + tempIn[j] = output[(j * 16) + i]; } HighbdIdct16(tempIn, tempOut, bd); - for (j = 0; j < 16; ++j) + for (int j = 0; j < 16; ++j) { - dest[j * stride + i] = HighbdClipPixelAdd(dest[j * stride + i], BitUtils.RoundPowerOfTwo(tempOut[j], 6), bd); + dest[(j * stride) + i] = HighbdClipPixelAdd(dest[(j * stride) + i], + BitUtils.RoundPowerOfTwo(tempOut[j], 6), bd); } } } - public static void HighbdIdct16x161Add(ReadOnlySpan input, Span dest, int stride, int bd) + public static void HighbdIdct16X161Add(ReadOnlySpan input, Span dest, int stride, int bd) { - int i, j; long a1; - int output = HighbdWrapLow(DctConstRoundShift(input[0] * (long)CosPi16_64), bd); + int output = HighbdWrapLow(DctConstRoundShift(input[0] * (long)CosPi1664), bd); - output = HighbdWrapLow(DctConstRoundShift(output * (long)CosPi16_64), bd); + output = HighbdWrapLow(DctConstRoundShift(output * (long)CosPi1664), bd); a1 = BitUtils.RoundPowerOfTwo(output, 6); - for (j = 0; j < 16; ++j) + for (int j = 0; j < 16; ++j) { - for (i = 0; i < 16; ++i) + for (int i = 0; i < 16; ++i) { dest[i] = HighbdClipPixelAdd(dest[i], a1, bd); } - dest = dest[stride..]; + dest = dest.Slice(stride); } } @@ -2423,8 +2394,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp if (DetectInvalidHighbdInput(input, 32) != 0) { Debug.Assert(false, "invalid highbd txfm input"); - output[..32].Clear(); - + output.Slice(0, 32).Clear(); return; } @@ -2446,43 +2416,43 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp step1[14] = input[14]; step1[15] = input[30]; - temp1 = input[1] * (long)CosPi31_64 - input[31] * (long)CosPi1_64; - temp2 = input[1] * (long)CosPi1_64 + input[31] * (long)CosPi31_64; + temp1 = (input[1] * (long)CosPi3164) - (input[31] * (long)CosPi164); + temp2 = (input[1] * (long)CosPi164) + (input[31] * (long)CosPi3164); step1[16] = HighbdWrapLow(DctConstRoundShift(temp1), bd); step1[31] = HighbdWrapLow(DctConstRoundShift(temp2), bd); - temp1 = input[17] * (long)CosPi15_64 - input[15] * (long)CosPi17_64; - temp2 = input[17] * (long)CosPi17_64 + input[15] * (long)CosPi15_64; + temp1 = (input[17] * (long)CosPi1564) - (input[15] * (long)CosPi1764); + temp2 = (input[17] * (long)CosPi1764) + (input[15] * (long)CosPi1564); step1[17] = HighbdWrapLow(DctConstRoundShift(temp1), bd); step1[30] = HighbdWrapLow(DctConstRoundShift(temp2), bd); - temp1 = input[9] * (long)CosPi23_64 - input[23] * (long)CosPi9_64; - temp2 = input[9] * (long)CosPi9_64 + input[23] * (long)CosPi23_64; + temp1 = (input[9] * (long)CosPi2364) - (input[23] * (long)CosPi964); + temp2 = (input[9] * (long)CosPi964) + (input[23] * (long)CosPi2364); step1[18] = HighbdWrapLow(DctConstRoundShift(temp1), bd); step1[29] = HighbdWrapLow(DctConstRoundShift(temp2), bd); - temp1 = input[25] * (long)CosPi7_64 - input[7] * (long)CosPi25_64; - temp2 = input[25] * (long)CosPi25_64 + input[7] * (long)CosPi7_64; + temp1 = (input[25] * (long)CosPi764) - (input[7] * (long)CosPi2564); + temp2 = (input[25] * (long)CosPi2564) + (input[7] * (long)CosPi764); step1[19] = HighbdWrapLow(DctConstRoundShift(temp1), bd); step1[28] = HighbdWrapLow(DctConstRoundShift(temp2), bd); - temp1 = input[5] * (long)CosPi27_64 - input[27] * (long)CosPi5_64; - temp2 = input[5] * (long)CosPi5_64 + input[27] * (long)CosPi27_64; + temp1 = (input[5] * (long)CosPi2764) - (input[27] * (long)CosPi564); + temp2 = (input[5] * (long)CosPi564) + (input[27] * (long)CosPi2764); step1[20] = HighbdWrapLow(DctConstRoundShift(temp1), bd); step1[27] = HighbdWrapLow(DctConstRoundShift(temp2), bd); - temp1 = input[21] * (long)CosPi11_64 - input[11] * (long)CosPi21_64; - temp2 = input[21] * (long)CosPi21_64 + input[11] * (long)CosPi11_64; + temp1 = (input[21] * (long)CosPi1164) - (input[11] * (long)CosPi2164); + temp2 = (input[21] * (long)CosPi2164) + (input[11] * (long)CosPi1164); step1[21] = HighbdWrapLow(DctConstRoundShift(temp1), bd); step1[26] = HighbdWrapLow(DctConstRoundShift(temp2), bd); - temp1 = input[13] * (long)CosPi19_64 - input[19] * (long)CosPi13_64; - temp2 = input[13] * (long)CosPi13_64 + input[19] * (long)CosPi19_64; + temp1 = (input[13] * (long)CosPi1964) - (input[19] * (long)CosPi1364); + temp2 = (input[13] * (long)CosPi1364) + (input[19] * (long)CosPi1964); step1[22] = HighbdWrapLow(DctConstRoundShift(temp1), bd); step1[25] = HighbdWrapLow(DctConstRoundShift(temp2), bd); - temp1 = input[29] * (long)CosPi3_64 - input[3] * (long)CosPi29_64; - temp2 = input[29] * (long)CosPi29_64 + input[3] * (long)CosPi3_64; + temp1 = (input[29] * (long)CosPi364) - (input[3] * (long)CosPi2964); + temp2 = (input[29] * (long)CosPi2964) + (input[3] * (long)CosPi364); step1[23] = HighbdWrapLow(DctConstRoundShift(temp1), bd); step1[24] = HighbdWrapLow(DctConstRoundShift(temp2), bd); @@ -2496,23 +2466,23 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp step2[6] = step1[6]; step2[7] = step1[7]; - temp1 = step1[8] * (long)CosPi30_64 - step1[15] * (long)CosPi2_64; - temp2 = step1[8] * (long)CosPi2_64 + step1[15] * (long)CosPi30_64; + temp1 = (step1[8] * (long)CosPi3064) - (step1[15] * (long)CosPi264); + temp2 = (step1[8] * (long)CosPi264) + (step1[15] * (long)CosPi3064); step2[8] = HighbdWrapLow(DctConstRoundShift(temp1), bd); step2[15] = HighbdWrapLow(DctConstRoundShift(temp2), bd); - temp1 = step1[9] * (long)CosPi14_64 - step1[14] * (long)CosPi18_64; - temp2 = step1[9] * (long)CosPi18_64 + step1[14] * (long)CosPi14_64; + temp1 = (step1[9] * (long)CosPi1464) - (step1[14] * (long)CosPi1864); + temp2 = (step1[9] * (long)CosPi1864) + (step1[14] * (long)CosPi1464); step2[9] = HighbdWrapLow(DctConstRoundShift(temp1), bd); step2[14] = HighbdWrapLow(DctConstRoundShift(temp2), bd); - temp1 = step1[10] * (long)CosPi22_64 - step1[13] * (long)CosPi10_64; - temp2 = step1[10] * (long)CosPi10_64 + step1[13] * (long)CosPi22_64; + temp1 = (step1[10] * (long)CosPi2264) - (step1[13] * (long)CosPi1064); + temp2 = (step1[10] * (long)CosPi1064) + (step1[13] * (long)CosPi2264); step2[10] = HighbdWrapLow(DctConstRoundShift(temp1), bd); step2[13] = HighbdWrapLow(DctConstRoundShift(temp2), bd); - temp1 = step1[11] * (long)CosPi6_64 - step1[12] * (long)CosPi26_64; - temp2 = step1[11] * (long)CosPi26_64 + step1[12] * (long)CosPi6_64; + temp1 = (step1[11] * (long)CosPi664) - (step1[12] * (long)CosPi2664); + temp2 = (step1[11] * (long)CosPi2664) + (step1[12] * (long)CosPi664); step2[11] = HighbdWrapLow(DctConstRoundShift(temp1), bd); step2[12] = HighbdWrapLow(DctConstRoundShift(temp2), bd); @@ -2539,12 +2509,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp step1[2] = step2[2]; step1[3] = step2[3]; - temp1 = step2[4] * (long)CosPi28_64 - step2[7] * (long)CosPi4_64; - temp2 = step2[4] * (long)CosPi4_64 + step2[7] * (long)CosPi28_64; + temp1 = (step2[4] * (long)CosPi2864) - (step2[7] * (long)CosPi464); + temp2 = (step2[4] * (long)CosPi464) + (step2[7] * (long)CosPi2864); step1[4] = HighbdWrapLow(DctConstRoundShift(temp1), bd); step1[7] = HighbdWrapLow(DctConstRoundShift(temp2), bd); - temp1 = step2[5] * (long)CosPi12_64 - step2[6] * (long)CosPi20_64; - temp2 = step2[5] * (long)CosPi20_64 + step2[6] * (long)CosPi12_64; + temp1 = (step2[5] * (long)CosPi1264) - (step2[6] * (long)CosPi2064); + temp2 = (step2[5] * (long)CosPi2064) + (step2[6] * (long)CosPi1264); step1[5] = HighbdWrapLow(DctConstRoundShift(temp1), bd); step1[6] = HighbdWrapLow(DctConstRoundShift(temp2), bd); @@ -2559,22 +2529,22 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp step1[16] = step2[16]; step1[31] = step2[31]; - temp1 = -step2[17] * (long)CosPi4_64 + step2[30] * (long)CosPi28_64; - temp2 = step2[17] * (long)CosPi28_64 + step2[30] * (long)CosPi4_64; + temp1 = (-step2[17] * (long)CosPi464) + (step2[30] * (long)CosPi2864); + temp2 = (step2[17] * (long)CosPi2864) + (step2[30] * (long)CosPi464); step1[17] = HighbdWrapLow(DctConstRoundShift(temp1), bd); step1[30] = HighbdWrapLow(DctConstRoundShift(temp2), bd); - temp1 = -step2[18] * (long)CosPi28_64 - step2[29] * (long)CosPi4_64; - temp2 = -step2[18] * (long)CosPi4_64 + step2[29] * (long)CosPi28_64; + temp1 = (-step2[18] * (long)CosPi2864) - (step2[29] * (long)CosPi464); + temp2 = (-step2[18] * (long)CosPi464) + (step2[29] * (long)CosPi2864); step1[18] = HighbdWrapLow(DctConstRoundShift(temp1), bd); step1[29] = HighbdWrapLow(DctConstRoundShift(temp2), bd); step1[19] = step2[19]; step1[20] = step2[20]; - temp1 = -step2[21] * (long)CosPi20_64 + step2[26] * (long)CosPi12_64; - temp2 = step2[21] * (long)CosPi12_64 + step2[26] * (long)CosPi20_64; + temp1 = (-step2[21] * (long)CosPi2064) + (step2[26] * (long)CosPi1264); + temp2 = (step2[21] * (long)CosPi1264) + (step2[26] * (long)CosPi2064); step1[21] = HighbdWrapLow(DctConstRoundShift(temp1), bd); step1[26] = HighbdWrapLow(DctConstRoundShift(temp2), bd); - temp1 = -step2[22] * (long)CosPi12_64 - step2[25] * (long)CosPi20_64; - temp2 = -step2[22] * (long)CosPi20_64 + step2[25] * (long)CosPi12_64; + temp1 = (-step2[22] * (long)CosPi1264) - (step2[25] * (long)CosPi2064); + temp2 = (-step2[22] * (long)CosPi2064) + (step2[25] * (long)CosPi1264); step1[22] = HighbdWrapLow(DctConstRoundShift(temp1), bd); step1[25] = HighbdWrapLow(DctConstRoundShift(temp2), bd); step1[23] = step2[23]; @@ -2583,12 +2553,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp step1[28] = step2[28]; // stage 4 - temp1 = (step1[0] + step1[1]) * (long)CosPi16_64; - temp2 = (step1[0] - step1[1]) * (long)CosPi16_64; + temp1 = (step1[0] + step1[1]) * (long)CosPi1664; + temp2 = (step1[0] - step1[1]) * (long)CosPi1664; step2[0] = HighbdWrapLow(DctConstRoundShift(temp1), bd); step2[1] = HighbdWrapLow(DctConstRoundShift(temp2), bd); - temp1 = step1[2] * (long)CosPi24_64 - step1[3] * (long)CosPi8_64; - temp2 = step1[2] * (long)CosPi8_64 + step1[3] * (long)CosPi24_64; + temp1 = (step1[2] * (long)CosPi2464) - (step1[3] * (long)CosPi864); + temp2 = (step1[2] * (long)CosPi864) + (step1[3] * (long)CosPi2464); step2[2] = HighbdWrapLow(DctConstRoundShift(temp1), bd); step2[3] = HighbdWrapLow(DctConstRoundShift(temp2), bd); step2[4] = HighbdWrapLow(step1[4] + step1[5], bd); @@ -2598,12 +2568,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp step2[8] = step1[8]; step2[15] = step1[15]; - temp1 = -step1[9] * (long)CosPi8_64 + step1[14] * (long)CosPi24_64; - temp2 = step1[9] * (long)CosPi24_64 + step1[14] * (long)CosPi8_64; + temp1 = (-step1[9] * (long)CosPi864) + (step1[14] * (long)CosPi2464); + temp2 = (step1[9] * (long)CosPi2464) + (step1[14] * (long)CosPi864); step2[9] = HighbdWrapLow(DctConstRoundShift(temp1), bd); step2[14] = HighbdWrapLow(DctConstRoundShift(temp2), bd); - temp1 = -step1[10] * (long)CosPi24_64 - step1[13] * (long)CosPi8_64; - temp2 = -step1[10] * (long)CosPi8_64 + step1[13] * (long)CosPi24_64; + temp1 = (-step1[10] * (long)CosPi2464) - (step1[13] * (long)CosPi864); + temp2 = (-step1[10] * (long)CosPi864) + (step1[13] * (long)CosPi2464); step2[10] = HighbdWrapLow(DctConstRoundShift(temp1), bd); step2[13] = HighbdWrapLow(DctConstRoundShift(temp2), bd); step2[11] = step1[11]; @@ -2633,8 +2603,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp step1[2] = HighbdWrapLow(step2[1] - step2[2], bd); step1[3] = HighbdWrapLow(step2[0] - step2[3], bd); step1[4] = step2[4]; - temp1 = (step2[6] - step2[5]) * (long)CosPi16_64; - temp2 = (step2[5] + step2[6]) * (long)CosPi16_64; + temp1 = (step2[6] - step2[5]) * (long)CosPi1664; + temp2 = (step2[5] + step2[6]) * (long)CosPi1664; step1[5] = HighbdWrapLow(DctConstRoundShift(temp1), bd); step1[6] = HighbdWrapLow(DctConstRoundShift(temp2), bd); step1[7] = step2[7]; @@ -2650,20 +2620,20 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp step1[16] = step2[16]; step1[17] = step2[17]; - temp1 = -step2[18] * (long)CosPi8_64 + step2[29] * (long)CosPi24_64; - temp2 = step2[18] * (long)CosPi24_64 + step2[29] * (long)CosPi8_64; + temp1 = (-step2[18] * (long)CosPi864) + (step2[29] * (long)CosPi2464); + temp2 = (step2[18] * (long)CosPi2464) + (step2[29] * (long)CosPi864); step1[18] = HighbdWrapLow(DctConstRoundShift(temp1), bd); step1[29] = HighbdWrapLow(DctConstRoundShift(temp2), bd); - temp1 = -step2[19] * (long)CosPi8_64 + step2[28] * (long)CosPi24_64; - temp2 = step2[19] * (long)CosPi24_64 + step2[28] * (long)CosPi8_64; + temp1 = (-step2[19] * (long)CosPi864) + (step2[28] * (long)CosPi2464); + temp2 = (step2[19] * (long)CosPi2464) + (step2[28] * (long)CosPi864); step1[19] = HighbdWrapLow(DctConstRoundShift(temp1), bd); step1[28] = HighbdWrapLow(DctConstRoundShift(temp2), bd); - temp1 = -step2[20] * (long)CosPi24_64 - step2[27] * (long)CosPi8_64; - temp2 = -step2[20] * (long)CosPi8_64 + step2[27] * (long)CosPi24_64; + temp1 = (-step2[20] * (long)CosPi2464) - (step2[27] * (long)CosPi864); + temp2 = (-step2[20] * (long)CosPi864) + (step2[27] * (long)CosPi2464); step1[20] = HighbdWrapLow(DctConstRoundShift(temp1), bd); step1[27] = HighbdWrapLow(DctConstRoundShift(temp2), bd); - temp1 = -step2[21] * (long)CosPi24_64 - step2[26] * (long)CosPi8_64; - temp2 = -step2[21] * (long)CosPi8_64 + step2[26] * (long)CosPi24_64; + temp1 = (-step2[21] * (long)CosPi2464) - (step2[26] * (long)CosPi864); + temp2 = (-step2[21] * (long)CosPi864) + (step2[26] * (long)CosPi2464); step1[21] = HighbdWrapLow(DctConstRoundShift(temp1), bd); step1[26] = HighbdWrapLow(DctConstRoundShift(temp2), bd); step1[22] = step2[22]; @@ -2684,12 +2654,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp step2[7] = HighbdWrapLow(step1[0] - step1[7], bd); step2[8] = step1[8]; step2[9] = step1[9]; - temp1 = (-step1[10] + step1[13]) * (long)CosPi16_64; - temp2 = (step1[10] + step1[13]) * (long)CosPi16_64; + temp1 = (-step1[10] + step1[13]) * (long)CosPi1664; + temp2 = (step1[10] + step1[13]) * (long)CosPi1664; step2[10] = HighbdWrapLow(DctConstRoundShift(temp1), bd); step2[13] = HighbdWrapLow(DctConstRoundShift(temp2), bd); - temp1 = (-step1[11] + step1[12]) * (long)CosPi16_64; - temp2 = (step1[11] + step1[12]) * (long)CosPi16_64; + temp1 = (-step1[11] + step1[12]) * (long)CosPi1664; + temp2 = (step1[11] + step1[12]) * (long)CosPi1664; step2[11] = HighbdWrapLow(DctConstRoundShift(temp1), bd); step2[12] = HighbdWrapLow(DctConstRoundShift(temp2), bd); step2[14] = step1[14]; @@ -2735,20 +2705,20 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp step1[17] = step2[17]; step1[18] = step2[18]; step1[19] = step2[19]; - temp1 = (-step2[20] + step2[27]) * (long)CosPi16_64; - temp2 = (step2[20] + step2[27]) * (long)CosPi16_64; + temp1 = (-step2[20] + step2[27]) * (long)CosPi1664; + temp2 = (step2[20] + step2[27]) * (long)CosPi1664; step1[20] = HighbdWrapLow(DctConstRoundShift(temp1), bd); step1[27] = HighbdWrapLow(DctConstRoundShift(temp2), bd); - temp1 = (-step2[21] + step2[26]) * (long)CosPi16_64; - temp2 = (step2[21] + step2[26]) * (long)CosPi16_64; + temp1 = (-step2[21] + step2[26]) * (long)CosPi1664; + temp2 = (step2[21] + step2[26]) * (long)CosPi1664; step1[21] = HighbdWrapLow(DctConstRoundShift(temp1), bd); step1[26] = HighbdWrapLow(DctConstRoundShift(temp2), bd); - temp1 = (-step2[22] + step2[25]) * (long)CosPi16_64; - temp2 = (step2[22] + step2[25]) * (long)CosPi16_64; + temp1 = (-step2[22] + step2[25]) * (long)CosPi1664; + temp2 = (step2[22] + step2[25]) * (long)CosPi1664; step1[22] = HighbdWrapLow(DctConstRoundShift(temp1), bd); step1[25] = HighbdWrapLow(DctConstRoundShift(temp2), bd); - temp1 = (-step2[23] + step2[24]) * (long)CosPi16_64; - temp2 = (step2[23] + step2[24]) * (long)CosPi16_64; + temp1 = (-step2[23] + step2[24]) * (long)CosPi1664; + temp2 = (step2[23] + step2[24]) * (long)CosPi1664; step1[23] = HighbdWrapLow(DctConstRoundShift(temp1), bd); step1[24] = HighbdWrapLow(DctConstRoundShift(temp2), bd); step1[28] = step2[28]; @@ -2792,19 +2762,18 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp } [SkipLocalsInit] - public static void HighbdIdct32x321024Add(ReadOnlySpan input, Span dest, int stride, int bd) + public static void HighbdIdct32X321024Add(ReadOnlySpan input, Span dest, int stride, int bd) { - int i, j; Span output = stackalloc int[32 * 32]; Span outptr = output; Span tempIn = stackalloc int[32]; Span tempOut = stackalloc int[32]; // Rows - for (i = 0; i < 32; ++i) + for (int i = 0; i < 32; ++i) { int zeroCoeff = 0; - for (j = 0; j < 32; ++j) + for (int j = 0; j < 32; ++j) { zeroCoeff |= input[j]; } @@ -2815,33 +2784,33 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp } else { - outptr[..32].Clear(); + outptr.Slice(0, 32).Clear(); } - input = input[32..]; - outptr = outptr[32..]; + input = input.Slice(32); + outptr = outptr.Slice(32); } // Columns - for (i = 0; i < 32; ++i) + for (int i = 0; i < 32; ++i) { - for (j = 0; j < 32; ++j) + for (int j = 0; j < 32; ++j) { - tempIn[j] = output[j * 32 + i]; + tempIn[j] = output[(j * 32) + i]; } HighbdIdct32(tempIn, tempOut, bd); - for (j = 0; j < 32; ++j) + for (int j = 0; j < 32; ++j) { - dest[j * stride + i] = HighbdClipPixelAdd(dest[j * stride + i], BitUtils.RoundPowerOfTwo(tempOut[j], 6), bd); + dest[(j * stride) + i] = HighbdClipPixelAdd(dest[(j * stride) + i], + BitUtils.RoundPowerOfTwo(tempOut[j], 6), bd); } } } [SkipLocalsInit] - public static void HighbdIdct32x32135Add(ReadOnlySpan input, Span dest, int stride, int bd) + public static void HighbdIdct32X32135Add(ReadOnlySpan input, Span dest, int stride, int bd) { - int i, j; Span output = stackalloc int[32 * 32]; Span outptr = output; Span tempIn = stackalloc int[32]; @@ -2851,35 +2820,34 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp // Rows // Only upper-left 16x16 has non-zero coeff - for (i = 0; i < 16; ++i) + for (int i = 0; i < 16; ++i) { HighbdIdct32(input, outptr, bd); - input = input[32..]; - outptr = outptr[32..]; + input = input.Slice(32); + outptr = outptr.Slice(32); } // Columns - for (i = 0; i < 32; ++i) + for (int i = 0; i < 32; ++i) { Span destT = dest; - for (j = 0; j < 32; ++j) + for (int j = 0; j < 32; ++j) { - tempIn[j] = output[j * 32 + i]; + tempIn[j] = output[(j * 32) + i]; } HighbdIdct32(tempIn, tempOut, bd); - for (j = 0; j < 32; ++j) + for (int j = 0; j < 32; ++j) { destT[i] = HighbdClipPixelAdd(destT[i], BitUtils.RoundPowerOfTwo(tempOut[j], 6), bd); - destT = destT[stride..]; + destT = destT.Slice(stride); } } } [SkipLocalsInit] - public static void HighbdIdct32x3234Add(ReadOnlySpan input, Span dest, int stride, int bd) + public static void HighbdIdct32X3234Add(ReadOnlySpan input, Span dest, int stride, int bd) { - int i, j; Span output = stackalloc int[32 * 32]; Span outptr = output; Span tempIn = stackalloc int[32]; @@ -2889,47 +2857,47 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp // Rows // Only upper-left 8x8 has non-zero coeff - for (i = 0; i < 8; ++i) + for (int i = 0; i < 8; ++i) { HighbdIdct32(input, outptr, bd); - input = input[32..]; - outptr = outptr[32..]; + input = input.Slice(32); + outptr = outptr.Slice(32); } // Columns - for (i = 0; i < 32; ++i) + for (int i = 0; i < 32; ++i) { - for (j = 0; j < 32; ++j) + for (int j = 0; j < 32; ++j) { - tempIn[j] = output[j * 32 + i]; + tempIn[j] = output[(j * 32) + i]; } HighbdIdct32(tempIn, tempOut, bd); - for (j = 0; j < 32; ++j) + for (int j = 0; j < 32; ++j) { - dest[j * stride + i] = HighbdClipPixelAdd(dest[j * stride + i], BitUtils.RoundPowerOfTwo(tempOut[j], 6), bd); + dest[(j * stride) + i] = HighbdClipPixelAdd(dest[(j * stride) + i], + BitUtils.RoundPowerOfTwo(tempOut[j], 6), bd); } } } - public static void HighbdIdct32x321Add(ReadOnlySpan input, Span dest, int stride, int bd) + public static void HighbdIdct32X321Add(ReadOnlySpan input, Span dest, int stride, int bd) { - int i, j; int a1; - int output = HighbdWrapLow(DctConstRoundShift(input[0] * (long)CosPi16_64), bd); + int output = HighbdWrapLow(DctConstRoundShift(input[0] * (long)CosPi1664), bd); - output = HighbdWrapLow(DctConstRoundShift(output * (long)CosPi16_64), bd); + output = HighbdWrapLow(DctConstRoundShift(output * (long)CosPi1664), bd); a1 = BitUtils.RoundPowerOfTwo(output, 6); - for (j = 0; j < 32; ++j) + for (int j = 0; j < 32; ++j) { - for (i = 0; i < 32; ++i) + for (int i = 0; i < 32; ++i) { dest[i] = HighbdClipPixelAdd(dest[i], a1, bd); } - dest = dest[stride..]; + dest = dest.Slice(stride); } } } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/LoopFilterAuto.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/LoopFilterAuto.cs new file mode 100644 index 000000000..fbd87e17f --- /dev/null +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/LoopFilterAuto.cs @@ -0,0 +1,229 @@ +using Ryujinx.Common.Memory; +using System; +using System.Runtime.Intrinsics.X86; + +namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp +{ + internal class LoopFilterAuto + { + public static void LpfHorizontal4( + ArrayPtr s, + int pitch, + ReadOnlySpan blimit, + ReadOnlySpan limit, + ReadOnlySpan thresh) + { + if (Sse2.IsSupported) + { + LoopFilterSse2.LpfHorizontal4(s, pitch, blimit, limit, thresh); + } + else + { + LoopFilterScalar.LpfHorizontal4(s, pitch, blimit[0], limit[0], thresh[0]); + } + } + + public static void LpfHorizontal4Dual( + ArrayPtr s, + int pitch, + ReadOnlySpan blimit0, + ReadOnlySpan limit0, + ReadOnlySpan thresh0, + ReadOnlySpan blimit1, + ReadOnlySpan limit1, + ReadOnlySpan thresh1) + { + if (Sse2.IsSupported) + { + LoopFilterSse2.LpfHorizontal4Dual(s, pitch, blimit0, limit0, thresh0, blimit1, limit1, thresh1); + } + else + { + LoopFilterScalar.LpfHorizontal4Dual(s, pitch, blimit0[0], limit0[0], thresh0[0], blimit1[0], limit1[0], + thresh1[0]); + } + } + + public static void LpfHorizontal8( + ArrayPtr s, + int pitch, + ReadOnlySpan blimit, + ReadOnlySpan limit, + ReadOnlySpan thresh) + { + if (Sse2.IsSupported) + { + LoopFilterSse2.LpfHorizontal8(s, pitch, blimit, limit, thresh); + } + else + { + LoopFilterScalar.LpfHorizontal8(s, pitch, blimit[0], limit[0], thresh[0]); + } + } + + public static void LpfHorizontal8Dual( + ArrayPtr s, + int pitch, + ReadOnlySpan blimit0, + ReadOnlySpan limit0, + ReadOnlySpan thresh0, + ReadOnlySpan blimit1, + ReadOnlySpan limit1, + ReadOnlySpan thresh1) + { + if (Sse2.IsSupported) + { + LoopFilterSse2.LpfHorizontal8Dual(s, pitch, blimit0, limit0, thresh0, blimit1, limit1, thresh1); + } + else + { + LoopFilterScalar.LpfHorizontal8Dual(s, pitch, blimit0[0], limit0[0], thresh0[0], blimit1[0], limit1[0], + thresh1[0]); + } + } + + public static void LpfHorizontal16( + ArrayPtr s, + int pitch, + ReadOnlySpan blimit, + ReadOnlySpan limit, + ReadOnlySpan thresh) + { + if (Sse2.IsSupported) + { + LoopFilterSse2.LpfHorizontal16(s, pitch, blimit, limit, thresh); + } + else + { + LoopFilterScalar.LpfHorizontal16(s, pitch, blimit[0], limit[0], thresh[0]); + } + } + + public static void LpfHorizontal16Dual( + ArrayPtr s, + int pitch, + ReadOnlySpan blimit, + ReadOnlySpan limit, + ReadOnlySpan thresh) + { + if (Sse2.IsSupported) + { + LoopFilterSse2.LpfHorizontal16Dual(s, pitch, blimit, limit, thresh); + } + else + { + LoopFilterScalar.LpfHorizontal16Dual(s, pitch, blimit[0], limit[0], thresh[0]); + } + } + + public static void LpfVertical4( + ArrayPtr s, + int pitch, + ReadOnlySpan blimit, + ReadOnlySpan limit, + ReadOnlySpan thresh) + { + if (Sse2.IsSupported) + { + LoopFilterSse2.LpfVertical4(s, pitch, blimit, limit, thresh); + } + else + { + LoopFilterScalar.LpfVertical4(s, pitch, blimit[0], limit[0], thresh[0]); + } + } + + public static void LpfVertical4Dual( + ArrayPtr s, + int pitch, + ReadOnlySpan blimit0, + ReadOnlySpan limit0, + ReadOnlySpan thresh0, + ReadOnlySpan blimit1, + ReadOnlySpan limit1, + ReadOnlySpan thresh1) + { + if (Sse2.IsSupported) + { + LoopFilterSse2.LpfVertical4Dual(s, pitch, blimit0, limit0, thresh0, blimit1, limit1, thresh1); + } + else + { + LoopFilterScalar.LpfVertical4Dual(s, pitch, blimit0[0], limit0[0], thresh0[0], blimit1[0], limit1[0], + thresh1[0]); + } + } + + public static void LpfVertical8( + ArrayPtr s, + int pitch, + ReadOnlySpan blimit, + ReadOnlySpan limit, + ReadOnlySpan thresh) + { + if (Sse2.IsSupported) + { + LoopFilterSse2.LpfVertical8(s, pitch, blimit, limit, thresh); + } + else + { + LoopFilterScalar.LpfVertical8(s, pitch, blimit[0], limit[0], thresh[0]); + } + } + + public static void LpfVertical8Dual( + ArrayPtr s, + int pitch, + ReadOnlySpan blimit0, + ReadOnlySpan limit0, + ReadOnlySpan thresh0, + ReadOnlySpan blimit1, + ReadOnlySpan limit1, + ReadOnlySpan thresh1) + { + if (Sse2.IsSupported) + { + LoopFilterSse2.LpfVertical8Dual(s, pitch, blimit0, limit0, thresh0, blimit1, limit1, thresh1); + } + else + { + LoopFilterScalar.LpfVertical8Dual(s, pitch, blimit0[0], limit0[0], thresh0[0], blimit1[0], limit1[0], + thresh1[0]); + } + } + + public static void LpfVertical16( + ArrayPtr s, + int pitch, + ReadOnlySpan blimit, + ReadOnlySpan limit, + ReadOnlySpan thresh) + { + if (Sse2.IsSupported) + { + LoopFilterSse2.LpfVertical16(s, pitch, blimit, limit, thresh); + } + else + { + LoopFilterScalar.LpfVertical16(s, pitch, blimit[0], limit[0], thresh[0]); + } + } + + public static void LpfVertical16Dual( + ArrayPtr s, + int pitch, + ReadOnlySpan blimit, + ReadOnlySpan limit, + ReadOnlySpan thresh) + { + if (Sse2.IsSupported) + { + LoopFilterSse2.LpfVertical16Dual(s, pitch, blimit, limit, thresh); + } + else + { + LoopFilterScalar.LpfVertical16Dual(s, pitch, blimit[0], limit[0], thresh[0]); + } + } + } +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/LoopFilterScalar.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/LoopFilterScalar.cs new file mode 100644 index 000000000..79c3f1235 --- /dev/null +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/LoopFilterScalar.cs @@ -0,0 +1,1093 @@ +using Ryujinx.Common.Memory; +using Ryujinx.Graphics.Nvdec.Vp9.Common; +using System; + +namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp +{ + internal static class LoopFilterScalar + { + private static sbyte ClampSbyte(int t) + { + return (sbyte)Math.Clamp(t, -128, 127); + } + + private static short ClampSbyteHigh(int t, int bd) + { + return bd switch + { + 10 => (short)Math.Clamp(t, -128 * 4, (128 * 4) - 1), + 12 => (short)Math.Clamp(t, -128 * 16, (128 * 16) - 1), + _ => (short)Math.Clamp(t, -128, 128 - 1) + }; + } + + // Should we apply any filter at all: 11111111 yes, 00000000 no + private static sbyte FilterMask( + byte limit, + byte blimit, + byte p3, + byte p2, + byte p1, + byte p0, + byte q0, + byte q1, + byte q2, + byte q3) + { + int mask = 0; + mask |= Math.Abs(p3 - p2) > limit ? -1 : 0; + mask |= Math.Abs(p2 - p1) > limit ? -1 : 0; + mask |= Math.Abs(p1 - p0) > limit ? -1 : 0; + mask |= Math.Abs(q1 - q0) > limit ? -1 : 0; + mask |= Math.Abs(q2 - q1) > limit ? -1 : 0; + mask |= Math.Abs(q3 - q2) > limit ? -1 : 0; + mask |= (Math.Abs(p0 - q0) * 2) + (Math.Abs(p1 - q1) / 2) > blimit ? -1 : 0; + return (sbyte)~mask; + } + + private static sbyte FlatMask4( + byte thresh, + byte p3, + byte p2, + byte p1, + byte p0, + byte q0, + byte q1, + byte q2, + byte q3) + { + int mask = 0; + mask |= Math.Abs(p1 - p0) > thresh ? -1 : 0; + mask |= Math.Abs(q1 - q0) > thresh ? -1 : 0; + mask |= Math.Abs(p2 - p0) > thresh ? -1 : 0; + mask |= Math.Abs(q2 - q0) > thresh ? -1 : 0; + mask |= Math.Abs(p3 - p0) > thresh ? -1 : 0; + mask |= Math.Abs(q3 - q0) > thresh ? -1 : 0; + return (sbyte)~mask; + } + + private static sbyte FlatMask5( + byte thresh, + byte p4, + byte p3, + byte p2, + byte p1, + byte p0, + byte q0, + byte q1, + byte q2, + byte q3, + byte q4) + { + int mask = ~FlatMask4(thresh, p3, p2, p1, p0, q0, q1, q2, q3); + mask |= Math.Abs(p4 - p0) > thresh ? -1 : 0; + mask |= Math.Abs(q4 - q0) > thresh ? -1 : 0; + return (sbyte)~mask; + } + + // Is there high edge variance internal edge: 11111111 yes, 00000000 no + private static sbyte HevMask( + byte thresh, + byte p1, + byte p0, + byte q0, + byte q1) + { + int hev = 0; + hev |= Math.Abs(p1 - p0) > thresh ? -1 : 0; + hev |= Math.Abs(q1 - q0) > thresh ? -1 : 0; + return (sbyte)hev; + } + + private static void Filter4( + sbyte mask, + byte thresh, + ref byte op1, + ref byte op0, + ref byte oq0, + ref byte oq1) + { + sbyte filter1, filter2; + + sbyte ps1 = (sbyte)(op1 ^ 0x80); + sbyte ps0 = (sbyte)(op0 ^ 0x80); + sbyte qs0 = (sbyte)(oq0 ^ 0x80); + sbyte qs1 = (sbyte)(oq1 ^ 0x80); + sbyte hev = HevMask(thresh, op1, op0, oq0, oq1); + + // add outer taps if we have high edge variance + sbyte filter = (sbyte)(ClampSbyte(ps1 - qs1) & hev); + + // inner taps + filter = (sbyte)(ClampSbyte(filter + (3 * (qs0 - ps0))) & mask); + + // save bottom 3 bits so that we round one side +4 and the other +3 + // if it equals 4 we'll set it to adjust by -1 to account for the fact + // we'd round it by 3 the other way + filter1 = (sbyte)(ClampSbyte(filter + 4) >> 3); + filter2 = (sbyte)(ClampSbyte(filter + 3) >> 3); + + oq0 = (byte)(ClampSbyte(qs0 - filter1) ^ 0x80); + op0 = (byte)(ClampSbyte(ps0 + filter2) ^ 0x80); + + // outer tap adjustments + filter = (sbyte)(BitUtils.RoundPowerOfTwo(filter1, 1) & ~hev); + + oq1 = (byte)(ClampSbyte(qs1 - filter) ^ 0x80); + op1 = (byte)(ClampSbyte(ps1 + filter) ^ 0x80); + } + + public static void LpfHorizontal4( + ArrayPtr s, + int pitch, + byte blimit, + byte limit, + byte thresh) + { + // loop filter designed to work using chars so that we can make maximum use + // of 8 bit simd instructions. + for (int i = 0; i < 8; ++i) + { + byte p3 = s[-4 * pitch], p2 = s[-3 * pitch], p1 = s[-2 * pitch], p0 = s[-pitch]; + byte q0 = s[0 * pitch], q1 = s[1 * pitch], q2 = s[2 * pitch], q3 = s[3 * pitch]; + sbyte mask = FilterMask(limit, blimit, p3, p2, p1, p0, q0, q1, q2, q3); + Filter4(mask, thresh, ref s[-2 * pitch], ref s[-1 * pitch], ref s[0], ref s[1 * pitch]); + s = s.Slice(1); + } + } + + public static void LpfHorizontal4Dual( + ArrayPtr s, + int pitch, + byte blimit0, + byte limit0, + byte thresh0, + byte blimit1, + byte limit1, + byte thresh1) + { + LpfHorizontal4(s, pitch, blimit0, limit0, thresh0); + LpfHorizontal4(s.Slice(8), pitch, blimit1, limit1, thresh1); + } + + public static void LpfVertical4( + ArrayPtr s, + int pitch, + byte blimit, + byte limit, + byte thresh) + { + // loop filter designed to work using chars so that we can make maximum use + // of 8 bit simd instructions. + for (int i = 0; i < 8; ++i) + { + byte p3 = s[-4], p2 = s[-3], p1 = s[-2], p0 = s[-1]; + byte q0 = s[0], q1 = s[1], q2 = s[2], q3 = s[3]; + sbyte mask = FilterMask(limit, blimit, p3, p2, p1, p0, q0, q1, q2, q3); + Filter4(mask, thresh, ref s[-2], ref s[-1], ref s[0], ref s[1]); + s = s.Slice(pitch); + } + } + + public static void LpfVertical4Dual( + ArrayPtr s, + int pitch, + byte blimit0, + byte limit0, + byte thresh0, + byte blimit1, + byte limit1, + byte thresh1) + { + LpfVertical4(s, pitch, blimit0, limit0, thresh0); + LpfVertical4(s.Slice(8 * pitch), pitch, blimit1, limit1, thresh1); + } + + private static void Filter8( + sbyte mask, + byte thresh, + bool flat, + ref byte op3, + ref byte op2, + ref byte op1, + ref byte op0, + ref byte oq0, + ref byte oq1, + ref byte oq2, + ref byte oq3) + { + if (flat && mask != 0) + { + byte p3 = op3, p2 = op2, p1 = op1, p0 = op0; + byte q0 = oq0, q1 = oq1, q2 = oq2, q3 = oq3; + + // 7-tap filter [1, 1, 1, 2, 1, 1, 1] + op2 = (byte)BitUtils.RoundPowerOfTwo(p3 + p3 + p3 + (2 * p2) + p1 + p0 + q0, 3); + op1 = (byte)BitUtils.RoundPowerOfTwo(p3 + p3 + p2 + (2 * p1) + p0 + q0 + q1, 3); + op0 = (byte)BitUtils.RoundPowerOfTwo(p3 + p2 + p1 + (2 * p0) + q0 + q1 + q2, 3); + oq0 = (byte)BitUtils.RoundPowerOfTwo(p2 + p1 + p0 + (2 * q0) + q1 + q2 + q3, 3); + oq1 = (byte)BitUtils.RoundPowerOfTwo(p1 + p0 + q0 + (2 * q1) + q2 + q3 + q3, 3); + oq2 = (byte)BitUtils.RoundPowerOfTwo(p0 + q0 + q1 + (2 * q2) + q3 + q3 + q3, 3); + } + else + { + Filter4(mask, thresh, ref op1, ref op0, ref oq0, ref oq1); + } + } + + public static void LpfHorizontal8( + ArrayPtr s, + int pitch, + byte blimit, + byte limit, + byte thresh) + { + // loop filter designed to work using chars so that we can make maximum use + // of 8 bit simd instructions. + for (int i = 0; i < 8; ++i) + { + byte p3 = s[-4 * pitch], p2 = s[-3 * pitch], p1 = s[-2 * pitch], p0 = s[-pitch]; + byte q0 = s[0 * pitch], q1 = s[1 * pitch], q2 = s[2 * pitch], q3 = s[3 * pitch]; + + sbyte mask = FilterMask(limit, blimit, p3, p2, p1, p0, q0, q1, q2, q3); + sbyte flat = FlatMask4(1, p3, p2, p1, p0, q0, q1, q2, q3); + Filter8( + mask, + thresh, + flat != 0, + ref s[-4 * pitch], + ref s[-3 * pitch], + ref s[-2 * pitch], + ref s[-1 * pitch], + ref s[0], + ref s[1 * pitch], + ref s[2 * pitch], + ref s[3 * pitch]); + s = s.Slice(1); + } + } + + public static void LpfHorizontal8Dual( + ArrayPtr s, + int pitch, + byte blimit0, + byte limit0, + byte thresh0, + byte blimit1, + byte limit1, + byte thresh1) + { + LpfHorizontal8(s, pitch, blimit0, limit0, thresh0); + LpfHorizontal8(s.Slice(8), pitch, blimit1, limit1, thresh1); + } + + public static void LpfVertical8( + ArrayPtr s, + int pitch, + byte blimit, + byte limit, + byte thresh) + { + for (int i = 0; i < 8; ++i) + { + byte p3 = s[-4], p2 = s[-3], p1 = s[-2], p0 = s[-1]; + byte q0 = s[0], q1 = s[1], q2 = s[2], q3 = s[3]; + sbyte mask = FilterMask(limit, blimit, p3, p2, p1, p0, q0, q1, q2, q3); + sbyte flat = FlatMask4(1, p3, p2, p1, p0, q0, q1, q2, q3); + Filter8( + mask, + thresh, + flat != 0, + ref s[-4], + ref s[-3], + ref s[-2], + ref s[-1], + ref s[0], + ref s[1], + ref s[2], + ref s[3]); + s = s.Slice(pitch); + } + } + + public static void LpfVertical8Dual( + ArrayPtr s, + int pitch, + byte blimit0, + byte limit0, + byte thresh0, + byte blimit1, + byte limit1, + byte thresh1) + { + LpfVertical8(s, pitch, blimit0, limit0, thresh0); + LpfVertical8(s.Slice(8 * pitch), pitch, blimit1, limit1, thresh1); + } + + private static void Filter16( + sbyte mask, + byte thresh, + bool flat, + bool flat2, + ref byte op7, + ref byte op6, + ref byte op5, + ref byte op4, + ref byte op3, + ref byte op2, + ref byte op1, + ref byte op0, + ref byte oq0, + ref byte oq1, + ref byte oq2, + ref byte oq3, + ref byte oq4, + ref byte oq5, + ref byte oq6, + ref byte oq7) + { + if (flat2 && flat && mask != 0) + { + byte p7 = op7, p6 = op6, p5 = op5, p4 = op4, p3 = op3, p2 = op2, p1 = op1, p0 = op0; + byte q0 = oq0, q1 = oq1, q2 = oq2, q3 = oq3, q4 = oq4, q5 = oq5, q6 = oq6, q7 = oq7; + + // 15-tap filter [1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1] + op6 = (byte)BitUtils.RoundPowerOfTwo( + (p7 * 7) + (p6 * 2) + p5 + p4 + p3 + p2 + p1 + p0 + q0, 4); + op5 = (byte)BitUtils.RoundPowerOfTwo( + (p7 * 6) + p6 + (p5 * 2) + p4 + p3 + p2 + p1 + p0 + q0 + q1, 4); + op4 = (byte)BitUtils.RoundPowerOfTwo( + (p7 * 5) + p6 + p5 + (p4 * 2) + p3 + p2 + p1 + p0 + q0 + q1 + q2, 4); + op3 = (byte)BitUtils.RoundPowerOfTwo( + (p7 * 4) + p6 + p5 + p4 + (p3 * 2) + p2 + p1 + p0 + q0 + q1 + q2 + q3, 4); + op2 = (byte)BitUtils.RoundPowerOfTwo( + (p7 * 3) + p6 + p5 + p4 + p3 + (p2 * 2) + p1 + p0 + q0 + q1 + q2 + q3 + q4, 4); + op1 = (byte)BitUtils.RoundPowerOfTwo( + (p7 * 2) + p6 + p5 + p4 + p3 + p2 + (p1 * 2) + p0 + q0 + q1 + q2 + q3 + q4 + q5, 4); + op0 = (byte)BitUtils.RoundPowerOfTwo( + p7 + p6 + p5 + p4 + p3 + p2 + p1 + (p0 * 2) + q0 + q1 + q2 + q3 + q4 + q5 + q6, 4); + oq0 = (byte)BitUtils.RoundPowerOfTwo( + p6 + p5 + p4 + p3 + p2 + p1 + p0 + (q0 * 2) + q1 + q2 + q3 + q4 + q5 + q6 + q7, 4); + oq1 = (byte)BitUtils.RoundPowerOfTwo( + p5 + p4 + p3 + p2 + p1 + p0 + q0 + (q1 * 2) + q2 + q3 + q4 + q5 + q6 + (q7 * 2), 4); + oq2 = (byte)BitUtils.RoundPowerOfTwo( + p4 + p3 + p2 + p1 + p0 + q0 + q1 + (q2 * 2) + q3 + q4 + q5 + q6 + (q7 * 3), 4); + oq3 = (byte)BitUtils.RoundPowerOfTwo( + p3 + p2 + p1 + p0 + q0 + q1 + q2 + (q3 * 2) + q4 + q5 + q6 + (q7 * 4), 4); + oq4 = (byte)BitUtils.RoundPowerOfTwo( + p2 + p1 + p0 + q0 + q1 + q2 + q3 + (q4 * 2) + q5 + q6 + (q7 * 5), 4); + oq5 = (byte)BitUtils.RoundPowerOfTwo( + p1 + p0 + q0 + q1 + q2 + q3 + q4 + (q5 * 2) + q6 + (q7 * 6), 4); + oq6 = (byte)BitUtils.RoundPowerOfTwo( + p0 + q0 + q1 + q2 + q3 + q4 + q5 + (q6 * 2) + (q7 * 7), 4); + } + else + { + Filter8(mask, thresh, flat, ref op3, ref op2, ref op1, ref op0, ref oq0, ref oq1, ref oq2, ref oq3); + } + } + + private static void MbLpfHorizontalEdgeW( + ArrayPtr s, + int pitch, + byte blimit, + byte limit, + byte thresh, + int count) + { + // loop filter designed to work using chars so that we can make maximum use + // of 8 bit simd instructions. + for (int i = 0; i < 8 * count; ++i) + { + byte p3 = s[-4 * pitch], p2 = s[-3 * pitch], p1 = s[-2 * pitch], p0 = s[-pitch]; + byte q0 = s[0 * pitch], q1 = s[1 * pitch], q2 = s[2 * pitch], q3 = s[3 * pitch]; + sbyte mask = FilterMask(limit, blimit, p3, p2, p1, p0, q0, q1, q2, q3); + sbyte flat = FlatMask4(1, p3, p2, p1, p0, q0, q1, q2, q3); + sbyte flat2 = FlatMask5( + 1, + s[-8 * pitch], + s[-7 * pitch], + s[-6 * pitch], + s[-5 * pitch], + p0, + q0, + s[4 * pitch], + s[5 * pitch], + s[6 * pitch], + s[7 * pitch]); + + Filter16( + mask, + thresh, + flat != 0, + flat2 != 0, + ref s[-8 * pitch], + ref s[-7 * pitch], + ref s[-6 * pitch], + ref s[-5 * pitch], + ref s[-4 * pitch], + ref s[-3 * pitch], + ref s[-2 * pitch], + ref s[-1 * pitch], + ref s[0], + ref s[1 * pitch], + ref s[2 * pitch], + ref s[3 * pitch], + ref s[4 * pitch], + ref s[5 * pitch], + ref s[6 * pitch], + ref s[7 * pitch]); + s = s.Slice(1); + } + } + + public static void LpfHorizontal16( + ArrayPtr s, + int pitch, + byte blimit, + byte limit, + byte thresh) + { + MbLpfHorizontalEdgeW(s, pitch, blimit, limit, thresh, 1); + } + + public static void LpfHorizontal16Dual( + ArrayPtr s, + int pitch, + byte blimit, + byte limit, + byte thresh) + { + MbLpfHorizontalEdgeW(s, pitch, blimit, limit, thresh, 2); + } + + private static void MbLpfVerticalEdgeW( + ArrayPtr s, + int pitch, + byte blimit, + byte limit, + byte thresh, + int count) + { + for (int i = 0; i < count; ++i) + { + byte p3 = s[-4], p2 = s[-3], p1 = s[-2], p0 = s[-1]; + byte q0 = s[0], q1 = s[1], q2 = s[2], q3 = s[3]; + sbyte mask = FilterMask(limit, blimit, p3, p2, p1, p0, q0, q1, q2, q3); + sbyte flat = FlatMask4(1, p3, p2, p1, p0, q0, q1, q2, q3); + sbyte flat2 = FlatMask5(1, s[-8], s[-7], s[-6], s[-5], p0, q0, s[4], s[5], s[6], s[7]); + + Filter16( + mask, + thresh, + flat != 0, + flat2 != 0, + ref s[-8], + ref s[-7], + ref s[-6], + ref s[-5], + ref s[-4], + ref s[-3], + ref s[-2], + ref s[-1], + ref s[0], + ref s[1], + ref s[2], + ref s[3], + ref s[4], + ref s[5], + ref s[6], + ref s[7]); + s = s.Slice(pitch); + } + } + + public static void LpfVertical16( + ArrayPtr s, + int pitch, + byte blimit, + byte limit, + byte thresh) + { + MbLpfVerticalEdgeW(s, pitch, blimit, limit, thresh, 8); + } + + public static void LpfVertical16Dual( + ArrayPtr s, + int pitch, + byte blimit, + byte limit, + byte thresh) + { + MbLpfVerticalEdgeW(s, pitch, blimit, limit, thresh, 16); + } + + // Should we apply any filter at all: 11111111 yes, 00000000 no ? + private static sbyte HighBdFilterMask( + byte limit, + byte blimit, + ushort p3, + ushort p2, + ushort p1, + ushort p0, + ushort q0, + ushort q1, + ushort q2, + ushort q3, + int bd) + { + int mask = 0; + short limit16 = (short)(limit << (bd - 8)); + short blimit16 = (short)(blimit << (bd - 8)); + mask |= Math.Abs(p3 - p2) > limit16 ? -1 : 0; + mask |= Math.Abs(p2 - p1) > limit16 ? -1 : 0; + mask |= Math.Abs(p1 - p0) > limit16 ? -1 : 0; + mask |= Math.Abs(q1 - q0) > limit16 ? -1 : 0; + mask |= Math.Abs(q2 - q1) > limit16 ? -1 : 0; + mask |= Math.Abs(q3 - q2) > limit16 ? -1 : 0; + mask |= (Math.Abs(p0 - q0) * 2) + (Math.Abs(p1 - q1) / 2) > blimit16 ? -1 : 0; + return (sbyte)~mask; + } + + private static sbyte HighBdFlatMask4( + byte thresh, + ushort p3, + ushort p2, + ushort p1, + ushort p0, + ushort q0, + ushort q1, + ushort q2, + ushort q3, + int bd) + { + int mask = 0; + short thresh16 = (short)(thresh << (bd - 8)); + mask |= Math.Abs(p1 - p0) > thresh16 ? -1 : 0; + mask |= Math.Abs(q1 - q0) > thresh16 ? -1 : 0; + mask |= Math.Abs(p2 - p0) > thresh16 ? -1 : 0; + mask |= Math.Abs(q2 - q0) > thresh16 ? -1 : 0; + mask |= Math.Abs(p3 - p0) > thresh16 ? -1 : 0; + mask |= Math.Abs(q3 - q0) > thresh16 ? -1 : 0; + return (sbyte)~mask; + } + + private static sbyte HighBdFlatMask5( + byte thresh, + ushort p4, + ushort p3, + ushort p2, + ushort p1, + ushort p0, + ushort q0, + ushort q1, + ushort q2, + ushort q3, + ushort q4, + int bd) + { + int mask = ~HighBdFlatMask4(thresh, p3, p2, p1, p0, q0, q1, q2, q3, bd); + short thresh16 = (short)(thresh << (bd - 8)); + mask |= Math.Abs(p4 - p0) > thresh16 ? -1 : 0; + mask |= Math.Abs(q4 - q0) > thresh16 ? -1 : 0; + return (sbyte)~mask; + } + + // Is there high edge variance internal edge: + // 11111111_11111111 yes, 00000000_00000000 no ? + private static short HighBdHevMask( + byte thresh, + ushort p1, + ushort p0, + ushort q0, + ushort q1, + int bd) + { + int hev = 0; + short thresh16 = (short)(thresh << (bd - 8)); + hev |= Math.Abs(p1 - p0) > thresh16 ? -1 : 0; + hev |= Math.Abs(q1 - q0) > thresh16 ? -1 : 0; + return (short)hev; + } + + private static void HighBdFilter4( + sbyte mask, + byte thresh, + ref ushort op1, + ref ushort op0, + ref ushort oq0, + ref ushort oq1, + int bd) + { + short filter1, filter2; + // ^0x80 equivalent to subtracting 0x80 from the values to turn them + // into -128 to +127 instead of 0 to 255. + int shift = bd - 8; + short ps1 = (short)((short)op1 - (0x80 << shift)); + short ps0 = (short)((short)op0 - (0x80 << shift)); + short qs0 = (short)((short)oq0 - (0x80 << shift)); + short qs1 = (short)((short)oq1 - (0x80 << shift)); + short hev = HighBdHevMask(thresh, op1, op0, oq0, oq1, bd); + + // Add outer taps if we have high edge variance. + short filter = (short)(ClampSbyteHigh(ps1 - qs1, bd) & hev); + + // Inner taps. + filter = (short)(ClampSbyteHigh(filter + (3 * (qs0 - ps0)), bd) & mask); + + // Save bottom 3 bits so that we round one side +4 and the other +3 + // if it equals 4 we'll set it to adjust by -1 to account for the fact + // we'd round it by 3 the other way. + filter1 = (short)(ClampSbyteHigh(filter + 4, bd) >> 3); + filter2 = (short)(ClampSbyteHigh(filter + 3, bd) >> 3); + + oq0 = (ushort)(ClampSbyteHigh(qs0 - filter1, bd) + (0x80 << shift)); + op0 = (ushort)(ClampSbyteHigh(ps0 + filter2, bd) + (0x80 << shift)); + + // Outer tap adjustments. + filter = (short)(BitUtils.RoundPowerOfTwo(filter1, 1) & ~hev); + + oq1 = (ushort)(ClampSbyteHigh(qs1 - filter, bd) + (0x80 << shift)); + op1 = (ushort)(ClampSbyteHigh(ps1 + filter, bd) + (0x80 << shift)); + } + + public static void HighBdLpfHorizontal4( + ArrayPtr s, + int pitch, + byte blimit, + byte limit, + byte thresh, + int bd) + { + // loop filter designed to work using chars so that we can make maximum use + // of 8 bit simd instructions. + for (int i = 0; i < 8; ++i) + { + ushort p3 = s[-4 * pitch]; + ushort p2 = s[-3 * pitch]; + ushort p1 = s[-2 * pitch]; + ushort p0 = s[-pitch]; + ushort q0 = s[0 * pitch]; + ushort q1 = s[1 * pitch]; + ushort q2 = s[2 * pitch]; + ushort q3 = s[3 * pitch]; + sbyte mask = HighBdFilterMask(limit, blimit, p3, p2, p1, p0, q0, q1, q2, q3, bd); + HighBdFilter4(mask, thresh, ref s[-2 * pitch], ref s[-1 * pitch], ref s[0], ref s[1 * pitch], bd); + s = s.Slice(1); + } + } + + public static void HighBdLpfHorizontal4Dual( + ArrayPtr s, + int pitch, + byte blimit0, + byte limit0, + byte thresh0, + byte blimit1, + byte limit1, + byte thresh1, + int bd) + { + HighBdLpfHorizontal4(s, pitch, blimit0, limit0, thresh0, bd); + HighBdLpfHorizontal4(s.Slice(8), pitch, blimit1, limit1, thresh1, bd); + } + + public static void HighBdLpfVertical4( + ArrayPtr s, + int pitch, + byte blimit, + byte limit, + byte thresh, + int bd) + { + // loop filter designed to work using chars so that we can make maximum use + // of 8 bit simd instructions. + for (int i = 0; i < 8; ++i) + { + ushort p3 = s[-4], p2 = s[-3], p1 = s[-2], p0 = s[-1]; + ushort q0 = s[0], q1 = s[1], q2 = s[2], q3 = s[3]; + sbyte mask = HighBdFilterMask(limit, blimit, p3, p2, p1, p0, q0, q1, q2, q3, bd); + HighBdFilter4(mask, thresh, ref s[-2], ref s[-1], ref s[0], ref s[1], bd); + s = s.Slice(pitch); + } + } + + public static void HighBdLpfVertical4Dual( + ArrayPtr s, + int pitch, + byte blimit0, + byte limit0, + byte thresh0, + byte blimit1, + byte limit1, + byte thresh1, + int bd) + { + HighBdLpfVertical4(s, pitch, blimit0, limit0, thresh0, bd); + HighBdLpfVertical4(s.Slice(8 * pitch), pitch, blimit1, limit1, thresh1, bd); + } + + private static void HighBdFilter8( + sbyte mask, + byte thresh, + bool flat, + ref ushort op3, + ref ushort op2, + ref ushort op1, + ref ushort op0, + ref ushort oq0, + ref ushort oq1, + ref ushort oq2, + ref ushort oq3, + int bd) + { + if (flat && mask != 0) + { + ushort p3 = op3, p2 = op2, p1 = op1, p0 = op0; + ushort q0 = oq0, q1 = oq1, q2 = oq2, q3 = oq3; + + // 7-tap filter [1, 1, 1, 2, 1, 1, 1] + op2 = (ushort)BitUtils.RoundPowerOfTwo(p3 + p3 + p3 + (2 * p2) + p1 + p0 + q0, 3); + op1 = (ushort)BitUtils.RoundPowerOfTwo(p3 + p3 + p2 + (2 * p1) + p0 + q0 + q1, 3); + op0 = (ushort)BitUtils.RoundPowerOfTwo(p3 + p2 + p1 + (2 * p0) + q0 + q1 + q2, 3); + oq0 = (ushort)BitUtils.RoundPowerOfTwo(p2 + p1 + p0 + (2 * q0) + q1 + q2 + q3, 3); + oq1 = (ushort)BitUtils.RoundPowerOfTwo(p1 + p0 + q0 + (2 * q1) + q2 + q3 + q3, 3); + oq2 = (ushort)BitUtils.RoundPowerOfTwo(p0 + q0 + q1 + (2 * q2) + q3 + q3 + q3, 3); + } + else + { + HighBdFilter4(mask, thresh, ref op1, ref op0, ref oq0, ref oq1, bd); + } + } + + public static void HighBdLpfHorizontal8( + ArrayPtr s, + int pitch, + byte blimit, + byte limit, + byte thresh, + int bd) + { + // loop filter designed to work using chars so that we can make maximum use + // of 8 bit simd instructions. + for (int i = 0; i < 8; ++i) + { + ushort p3 = s[-4 * pitch], p2 = s[-3 * pitch], p1 = s[-2 * pitch], p0 = s[-pitch]; + ushort q0 = s[0 * pitch], q1 = s[1 * pitch], q2 = s[2 * pitch], q3 = s[3 * pitch]; + + sbyte mask = HighBdFilterMask(limit, blimit, p3, p2, p1, p0, q0, q1, q2, q3, bd); + sbyte flat = HighBdFlatMask4(1, p3, p2, p1, p0, q0, q1, q2, q3, bd); + HighBdFilter8( + mask, + thresh, + flat != 0, + ref s[-4 * pitch], + ref s[-3 * pitch], + ref s[-2 * pitch], + ref s[-1 * pitch], + ref s[0], + ref s[1 * pitch], + ref s[2 * pitch], + ref s[3 * pitch], + bd); + s = s.Slice(1); + } + } + + public static void HighBdLpfHorizontal8Dual( + ArrayPtr s, + int pitch, + byte blimit0, + byte limit0, + byte thresh0, + byte blimit1, + byte limit1, + byte thresh1, + int bd) + { + HighBdLpfHorizontal8(s, pitch, blimit0, limit0, thresh0, bd); + HighBdLpfHorizontal8(s.Slice(8), pitch, blimit1, limit1, thresh1, bd); + } + + public static void HighBdLpfVertical8( + ArrayPtr s, + int pitch, + byte blimit, + byte limit, + byte thresh, + int bd) + { + for (int i = 0; i < 8; ++i) + { + ushort p3 = s[-4], p2 = s[-3], p1 = s[-2], p0 = s[-1]; + ushort q0 = s[0], q1 = s[1], q2 = s[2], q3 = s[3]; + sbyte mask = HighBdFilterMask(limit, blimit, p3, p2, p1, p0, q0, q1, q2, q3, bd); + sbyte flat = HighBdFlatMask4(1, p3, p2, p1, p0, q0, q1, q2, q3, bd); + HighBdFilter8( + mask, + thresh, + flat != 0, + ref s[-4], + ref s[-3], + ref s[-2], + ref s[-1], + ref s[0], + ref s[1], + ref s[2], + ref s[3], + bd); + s = s.Slice(pitch); + } + } + + public static void HighBdLpfVertical8Dual( + ArrayPtr s, + int pitch, + byte blimit0, + byte limit0, + byte thresh0, + byte blimit1, + byte limit1, + byte thresh1, + int bd) + { + HighBdLpfVertical8(s, pitch, blimit0, limit0, thresh0, bd); + HighBdLpfVertical8(s.Slice(8 * pitch), pitch, blimit1, limit1, thresh1, bd); + } + + private static void HighBdFilter16( + sbyte mask, + byte thresh, + bool flat, + bool flat2, + ref ushort op7, + ref ushort op6, + ref ushort op5, + ref ushort op4, + ref ushort op3, + ref ushort op2, + ref ushort op1, + ref ushort op0, + ref ushort oq0, + ref ushort oq1, + ref ushort oq2, + ref ushort oq3, + ref ushort oq4, + ref ushort oq5, + ref ushort oq6, + ref ushort oq7, + int bd) + { + if (flat2 && flat && mask != 0) + { + ushort p7 = op7; + ushort p6 = op6; + ushort p5 = op5; + ushort p4 = op4; + ushort p3 = op3; + ushort p2 = op2; + ushort p1 = op1; + ushort p0 = op0; + ushort q0 = oq0; + ushort q1 = oq1; + ushort q2 = oq2; + ushort q3 = oq3; + ushort q4 = oq4; + ushort q5 = oq5; + ushort q6 = oq6; + ushort q7 = oq7; + + // 15-tap filter [1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1] + op6 = (ushort)BitUtils.RoundPowerOfTwo( + (p7 * 7) + (p6 * 2) + p5 + p4 + p3 + p2 + p1 + p0 + q0, 4); + op5 = (ushort)BitUtils.RoundPowerOfTwo( + (p7 * 6) + p6 + (p5 * 2) + p4 + p3 + p2 + p1 + p0 + q0 + q1, 4); + op4 = (ushort)BitUtils.RoundPowerOfTwo( + (p7 * 5) + p6 + p5 + (p4 * 2) + p3 + p2 + p1 + p0 + q0 + q1 + q2, 4); + op3 = (ushort)BitUtils.RoundPowerOfTwo( + (p7 * 4) + p6 + p5 + p4 + (p3 * 2) + p2 + p1 + p0 + q0 + q1 + q2 + q3, 4); + op2 = (ushort)BitUtils.RoundPowerOfTwo( + (p7 * 3) + p6 + p5 + p4 + p3 + (p2 * 2) + p1 + p0 + q0 + q1 + q2 + q3 + q4, 4); + op1 = (ushort)BitUtils.RoundPowerOfTwo( + (p7 * 2) + p6 + p5 + p4 + p3 + p2 + (p1 * 2) + p0 + q0 + q1 + q2 + q3 + q4 + q5, 4); + op0 = (ushort)BitUtils.RoundPowerOfTwo( + p7 + p6 + p5 + p4 + p3 + p2 + p1 + (p0 * 2) + q0 + q1 + q2 + q3 + q4 + q5 + q6, 4); + oq0 = (ushort)BitUtils.RoundPowerOfTwo( + p6 + p5 + p4 + p3 + p2 + p1 + p0 + (q0 * 2) + q1 + q2 + q3 + q4 + q5 + q6 + q7, 4); + oq1 = (ushort)BitUtils.RoundPowerOfTwo( + p5 + p4 + p3 + p2 + p1 + p0 + q0 + (q1 * 2) + q2 + q3 + q4 + q5 + q6 + (q7 * 2), 4); + oq2 = (ushort)BitUtils.RoundPowerOfTwo( + p4 + p3 + p2 + p1 + p0 + q0 + q1 + (q2 * 2) + q3 + q4 + q5 + q6 + (q7 * 3), 4); + oq3 = (ushort)BitUtils.RoundPowerOfTwo( + p3 + p2 + p1 + p0 + q0 + q1 + q2 + (q3 * 2) + q4 + q5 + q6 + (q7 * 4), 4); + oq4 = (ushort)BitUtils.RoundPowerOfTwo( + p2 + p1 + p0 + q0 + q1 + q2 + q3 + (q4 * 2) + q5 + q6 + (q7 * 5), 4); + oq5 = (ushort)BitUtils.RoundPowerOfTwo( + p1 + p0 + q0 + q1 + q2 + q3 + q4 + (q5 * 2) + q6 + (q7 * 6), 4); + oq6 = (ushort)BitUtils.RoundPowerOfTwo( + p0 + q0 + q1 + q2 + q3 + q4 + q5 + (q6 * 2) + (q7 * 7), 4); + } + else + { + HighBdFilter8(mask, thresh, flat, ref op3, ref op2, ref op1, ref op0, ref oq0, ref oq1, ref oq2, + ref oq3, bd); + } + } + + private static void HighBdMbLpfHorizontalEdgeW( + ArrayPtr s, + int pitch, + byte blimit, + byte limit, + byte thresh, + int count, + int bd) + { + // loop filter designed to work using chars so that we can make maximum use + // of 8 bit simd instructions. + for (int i = 0; i < 8 * count; ++i) + { + ushort p3 = s[-4 * pitch]; + ushort p2 = s[-3 * pitch]; + ushort p1 = s[-2 * pitch]; + ushort p0 = s[-pitch]; + ushort q0 = s[0 * pitch]; + ushort q1 = s[1 * pitch]; + ushort q2 = s[2 * pitch]; + ushort q3 = s[3 * pitch]; + sbyte mask = HighBdFilterMask(limit, blimit, p3, p2, p1, p0, q0, q1, q2, q3, bd); + sbyte flat = HighBdFlatMask4(1, p3, p2, p1, p0, q0, q1, q2, q3, bd); + sbyte flat2 = HighBdFlatMask5( + 1, + s[-8 * pitch], + s[-7 * pitch], + s[-6 * pitch], + s[-5 * pitch], + p0, + q0, + s[4 * pitch], + s[5 * pitch], + s[6 * pitch], + s[7 * pitch], + bd); + + HighBdFilter16( + mask, + thresh, + flat != 0, + flat2 != 0, + ref s[-8 * pitch], + ref s[-7 * pitch], + ref s[-6 * pitch], + ref s[-5 * pitch], + ref s[-4 * pitch], + ref s[-3 * pitch], + ref s[-2 * pitch], + ref s[-1 * pitch], + ref s[0], + ref s[1 * pitch], + ref s[2 * pitch], + ref s[3 * pitch], + ref s[4 * pitch], + ref s[5 * pitch], + ref s[6 * pitch], + ref s[7 * pitch], + bd); + s = s.Slice(1); + } + } + + public static void HighBdLpfHorizontal16( + ArrayPtr s, + int pitch, + byte blimit, + byte limit, + byte thresh, + int bd) + { + HighBdMbLpfHorizontalEdgeW(s, pitch, blimit, limit, thresh, 1, bd); + } + + public static void HighBdLpfHorizontal16Dual( + ArrayPtr s, + int pitch, + byte blimit, + byte limit, + byte thresh, + int bd) + { + HighBdMbLpfHorizontalEdgeW(s, pitch, blimit, limit, thresh, 2, bd); + } + + private static void HighBdMbLpfVerticalEdgeW( + ArrayPtr s, + int pitch, + byte blimit, + byte limit, + byte thresh, + int count, + int bd) + { + for (int i = 0; i < count; ++i) + { + ushort p3 = s[-4]; + ushort p2 = s[-3]; + ushort p1 = s[-2]; + ushort p0 = s[-1]; + ushort q0 = s[0]; + ushort q1 = s[1]; + ushort q2 = s[2]; + ushort q3 = s[3]; + sbyte mask = HighBdFilterMask(limit, blimit, p3, p2, p1, p0, q0, q1, q2, q3, bd); + sbyte flat = HighBdFlatMask4(1, p3, p2, p1, p0, q0, q1, q2, q3, bd); + sbyte flat2 = HighBdFlatMask5(1, s[-8], s[-7], s[-6], s[-5], p0, q0, s[4], s[5], s[6], s[7], bd); + + HighBdFilter16( + mask, + thresh, + flat != 0, + flat2 != 0, + ref s[-8], + ref s[-7], + ref s[-6], + ref s[-5], + ref s[-4], + ref s[-3], + ref s[-2], + ref s[-1], + ref s[0], + ref s[1], + ref s[2], + ref s[3], + ref s[4], + ref s[5], + ref s[6], + ref s[7], + bd); + s = s.Slice(pitch); + } + } + + public static void HighBdLpfVertical16( + ArrayPtr s, + int pitch, + byte blimit, + byte limit, + byte thresh, + int bd) + { + HighBdMbLpfVerticalEdgeW(s, pitch, blimit, limit, thresh, 8, bd); + } + + public static void HighBdLpfVertical16Dual( + ArrayPtr s, + int pitch, + byte blimit, + byte limit, + byte thresh, + int bd) + { + HighBdMbLpfVerticalEdgeW(s, pitch, blimit, limit, thresh, 16, bd); + } + } +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/LoopFilterSse2.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/LoopFilterSse2.cs new file mode 100644 index 000000000..0d9f496ae --- /dev/null +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/LoopFilterSse2.cs @@ -0,0 +1,1837 @@ +using Ryujinx.Common.Memory; +using System; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; + +namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp +{ + internal static class LoopFilterSse2 + { + private static Vector128 AbsDiff(Vector128 a, Vector128 b) + { + return Sse2.Or(Sse2.SubtractSaturate(a, b), Sse2.SubtractSaturate(b, a)); + } + + private static void FilterHevMask( + Vector128 q1P1, + Vector128 q0P0, + Vector128 p3P2, + Vector128 p2P1, + Vector128 p1P0, + Vector128 q3Q2, + Vector128 q2Q1, + Vector128 q1Q0, + Vector128 limitV, + Vector128 threshV, + out Vector128 hev, + out Vector128 mask) + { + /* (abs(q1 - q0), abs(p1 - p0) */ + Vector128 flat = AbsDiff(q1P1, q0P0); + /* abs(p1 - q1), abs(p0 - q0) */ + Vector128 absP1Q1P0Q0 = AbsDiff(p1P0, q1Q0); + Vector128 absP0Q0, absP1Q1, work; + + /* const uint8_t hev = hev_mask(thresh, *op1, *op0, *oq0, *oq1); */ + hev = Sse2.UnpackLow(Sse2.Max(flat, Sse2.ShiftRightLogical128BitLane(flat, 8)), Vector128.Zero); + hev = Sse2.CompareGreaterThan(hev.AsInt16(), threshV.AsInt16()).AsByte(); + hev = Sse2.PackSignedSaturate(hev.AsInt16(), hev.AsInt16()).AsByte(); + + /* const int8_t mask = filter_mask(*limit, *blimit, p3, p2, p1, p0, q0, q1, q2, q3); */ + absP0Q0 = Sse2.AddSaturate(absP1Q1P0Q0, absP1Q1P0Q0); /* abs(p0 - q0) * 2 */ + absP1Q1 = Sse2.UnpackHigh(absP1Q1P0Q0, absP1Q1P0Q0); /* abs(p1 - q1) */ + absP1Q1 = Sse2.ShiftRightLogical(absP1Q1.AsInt16(), 9).AsByte(); + absP1Q1 = Sse2.PackSignedSaturate(absP1Q1.AsInt16(), absP1Q1.AsInt16()).AsByte(); /* abs(p1 - q1) / 2 */ + /* abs(p0 - q0) * 2 + abs(p1 - q1) / 2 */ + mask = Sse2.AddSaturate(absP0Q0, absP1Q1); + /* abs(p3 - p2), abs(p2 - p1) */ + work = AbsDiff(p3P2, p2P1); + flat = Sse2.Max(work, flat); + /* abs(q3 - q2), abs(q2 - q1) */ + work = AbsDiff(q3Q2, q2Q1); + flat = Sse2.Max(work, flat); + flat = Sse2.Max(flat, Sse2.ShiftRightLogical128BitLane(flat, 8)); + mask = Sse2.UnpackLow(mask.AsInt64(), flat.AsInt64()).AsByte(); + mask = Sse2.SubtractSaturate(mask, limitV); + mask = Sse2.CompareEqual(mask, Vector128.Zero); + mask = Sse2.And(mask, Sse2.ShiftRightLogical128BitLane(mask, 8)); + } + + private static void Filter4( + Vector128 p1P0, + Vector128 q1Q0, + Vector128 hev, + Vector128 mask, + Vector128 ff, + out Vector128 ps1Ps0, + out Vector128 qs1Qs0) + { + Vector128 t3T4 = Vector128.Create( + 4, 4, 4, 4, + 4, 4, 4, 4, + 3, 3, 3, 3, + 3, 3, 3, (byte)3); + Vector128 t80 = Vector128.Create((byte)0x80); + Vector128 filter, filter2Filter1, work; + + ps1Ps0 = Sse2.Xor(p1P0, t80); /* ^ 0x80 */ + qs1Qs0 = Sse2.Xor(q1Q0, t80); + + /* int8_t filter = signed_char_clamp(ps1 - qs1) & hev; */ + work = Sse2.SubtractSaturate(ps1Ps0.AsSByte(), qs1Qs0.AsSByte()).AsByte(); + filter = Sse2.And(Sse2.ShiftRightLogical128BitLane(work, 8), hev); + /* filter = signed_char_clamp(filter + 3 * (qs0 - ps0)) & mask; */ + filter = Sse2.SubtractSaturate(filter.AsSByte(), work.AsSByte()).AsByte(); + filter = Sse2.SubtractSaturate(filter.AsSByte(), work.AsSByte()).AsByte(); + filter = Sse2.SubtractSaturate(filter.AsSByte(), work.AsSByte()).AsByte(); /* + 3 * (qs0 - ps0) */ + filter = Sse2.And(filter, mask); /* & mask */ + filter = Sse2.UnpackLow(filter.AsInt64(), filter.AsInt64()).AsByte(); + + /* filter1 = signed_char_clamp(filter + 4) >> 3; */ + /* filter2 = signed_char_clamp(filter + 3) >> 3; */ + filter2Filter1 = Sse2.AddSaturate(filter.AsSByte(), t3T4.AsSByte()).AsByte(); /* signed_char_clamp */ + filter = Sse2.UnpackHigh(filter2Filter1, filter2Filter1); + filter2Filter1 = Sse2.UnpackLow(filter2Filter1, filter2Filter1); + filter2Filter1 = Sse2.ShiftRightArithmetic(filter2Filter1.AsInt16(), 11).AsByte(); /* >> 3 */ + filter = Sse2.ShiftRightArithmetic(filter.AsInt16(), 11).AsByte(); /* >> 3 */ + filter2Filter1 = Sse2.PackSignedSaturate(filter2Filter1.AsInt16(), filter.AsInt16()).AsByte(); + + /* filter = ROUND_POWER_OF_TWO(filter1, 1) & ~hev; */ + filter = Sse2.SubtractSaturate(filter2Filter1.AsSByte(), ff.AsSByte()).AsByte(); /* + 1 */ + filter = Sse2.UnpackLow(filter, filter); + filter = Sse2.ShiftRightArithmetic(filter.AsInt16(), 9).AsByte(); /* round */ + filter = Sse2.PackSignedSaturate(filter.AsInt16(), filter.AsInt16()).AsByte(); + filter = Sse2.AndNot(hev, filter); + + hev = Sse2.UnpackHigh(filter2Filter1.AsInt64(), filter.AsInt64()).AsByte(); + filter2Filter1 = Sse2.UnpackLow(filter2Filter1.AsInt64(), filter.AsInt64()).AsByte(); + + /* signed_char_clamp(qs1 - filter), signed_char_clamp(qs0 - filter1) */ + qs1Qs0 = Sse2.SubtractSaturate(qs1Qs0.AsSByte(), filter2Filter1.AsSByte()).AsByte(); + /* signed_char_clamp(ps1 + filter), signed_char_clamp(ps0 + filter2) */ + ps1Ps0 = Sse2.AddSaturate(ps1Ps0.AsSByte(), hev.AsSByte()).AsByte(); + qs1Qs0 = Sse2.Xor(qs1Qs0, t80); /* ^ 0x80 */ + ps1Ps0 = Sse2.Xor(ps1Ps0, t80); /* ^ 0x80 */ + } + + public static unsafe void LpfHorizontal4( + ArrayPtr s, + int pitch, + ReadOnlySpan blimit, + ReadOnlySpan limit, + ReadOnlySpan thresh) + { + Vector128 zero = Vector128.Zero; + Vector128 limitV, threshV; + + fixed (byte* pBLimit = blimit, pLimit = limit, pThresh = thresh) + { + limitV = Sse2.UnpackLow( + Sse2.LoadScalarVector128((long*)pBLimit), + Sse2.LoadScalarVector128((long*)pLimit)).AsByte(); + threshV = Sse2.UnpackLow(Sse2.LoadScalarVector128((long*)pThresh).AsByte(), zero); + } + + Vector128 ff = Sse2.CompareEqual(zero, zero); + Vector128 q1P1, q0P0, p3P2, p2P1, p1P0, q3Q2, q2Q1, q1Q0, ps1Ps0, qs1Qs0; + Vector128 mask, hev; + + p3P2 = Sse2.UnpackLow(Sse2.LoadScalarVector128((long*)(s.ToPointer() - (3 * pitch))), + Sse2.LoadScalarVector128((long*)(s.ToPointer() - (4 * pitch)))).AsByte(); + q1P1 = Sse2.UnpackLow(Sse2.LoadScalarVector128((long*)(s.ToPointer() - (2 * pitch))), + Sse2.LoadScalarVector128((long*)(s.ToPointer() + (1 * pitch)))).AsByte(); + q0P0 = Sse2.UnpackLow(Sse2.LoadScalarVector128((long*)(s.ToPointer() - (1 * pitch))), + Sse2.LoadScalarVector128((long*)(s.ToPointer() + (0 * pitch)))).AsByte(); + q3Q2 = Sse2.UnpackLow(Sse2.LoadScalarVector128((long*)(s.ToPointer() + (2 * pitch))), + Sse2.LoadScalarVector128((long*)(s.ToPointer() + (3 * pitch)))).AsByte(); + p1P0 = Sse2.UnpackLow(q0P0.AsInt64(), q1P1.AsInt64()).AsByte(); + p2P1 = Sse2.UnpackLow(q1P1.AsInt64(), p3P2.AsInt64()).AsByte(); + q1Q0 = Sse2.UnpackHigh(q0P0.AsInt64(), q1P1.AsInt64()).AsByte(); + q2Q1 = Sse2.UnpackLow(Sse2.ShiftRightLogical128BitLane(q1P1, 8).AsInt64(), q3Q2.AsInt64()).AsByte(); + + FilterHevMask(q1P1, q0P0, p3P2, p2P1, p1P0, q3Q2, q2Q1, q1Q0, limitV, threshV, out hev, out mask); + Filter4(p1P0, q1Q0, hev, mask, ff, out ps1Ps0, out qs1Qs0); + + Sse.StoreHigh((float*)(s.ToPointer() - (2 * pitch)), ps1Ps0.AsSingle()); // *op1 + Sse2.StoreScalar((long*)(s.ToPointer() - (1 * pitch)), ps1Ps0.AsInt64()); // *op0 + Sse2.StoreScalar((long*)(s.ToPointer() + (0 * pitch)), qs1Qs0.AsInt64()); // *oq0 + Sse.StoreHigh((float*)(s.ToPointer() + (1 * pitch)), qs1Qs0.AsSingle()); // *oq1 + } + + public static unsafe void LpfVertical4( + ArrayPtr s, + int pitch, + ReadOnlySpan blimit, + ReadOnlySpan limit, + ReadOnlySpan thresh) + { + Vector128 zero = Vector128.Zero; + Vector128 limitV, threshV; + + fixed (byte* pBLimit = blimit, pLimit = limit, pThresh = thresh) + { + limitV = Sse2.UnpackLow( + Sse2.LoadScalarVector128((long*)pBLimit).AsInt64(), + Sse2.LoadScalarVector128((long*)pLimit).AsInt64()).AsByte(); + threshV = Sse2.UnpackLow(Sse2.LoadScalarVector128((long*)pThresh).AsByte(), zero); + } + + Vector128 ff = Sse2.CompareEqual(zero, zero); + Vector128 x0, x1, x2, x3; + Vector128 q1P1, q0P0, p3P2, p2P1, p1P0, q3Q2, q2Q1, q1Q0, ps1Ps0, qs1Qs0; + Vector128 mask, hev; + + // 00 10 01 11 02 12 03 13 04 14 05 15 06 16 07 17 + q1Q0 = Sse2.UnpackLow( + Sse2.LoadScalarVector128((long*)(s.ToPointer() + (0 * pitch) - 4)).AsByte(), + Sse2.LoadScalarVector128((long*)(s.ToPointer() + (1 * pitch) - 4)).AsByte()); + + // 20 30 21 31 22 32 23 33 24 34 25 35 26 36 27 37 + x1 = Sse2.UnpackLow( + Sse2.LoadScalarVector128((long*)(s.ToPointer() + (2 * pitch) - 4)).AsByte(), + Sse2.LoadScalarVector128((long*)(s.ToPointer() + (3 * pitch) - 4)).AsByte()); + + // 40 50 41 51 42 52 43 53 44 54 45 55 46 56 47 57 + x2 = Sse2.UnpackLow( + Sse2.LoadScalarVector128((long*)(s.ToPointer() + (4 * pitch) - 4)).AsByte(), + Sse2.LoadScalarVector128((long*)(s.ToPointer() + (5 * pitch) - 4)).AsByte()); + + // 60 70 61 71 62 72 63 73 64 74 65 75 66 76 67 77 + x3 = Sse2.UnpackLow( + Sse2.LoadScalarVector128((long*)(s.ToPointer() + (6 * pitch) - 4)).AsByte(), + Sse2.LoadScalarVector128((long*)(s.ToPointer() + (7 * pitch) - 4)).AsByte()); + + // Transpose 8x8 + // 00 10 20 30 01 11 21 31 02 12 22 32 03 13 23 33 + p1P0 = Sse2.UnpackLow(q1Q0.AsInt16(), x1.AsInt16()).AsByte(); + // 40 50 60 70 41 51 61 71 42 52 62 72 43 53 63 73 + x0 = Sse2.UnpackLow(x2.AsInt16(), x3.AsInt16()).AsByte(); + // 00 10 20 30 40 50 60 70 01 11 21 31 41 51 61 71 + p3P2 = Sse2.UnpackLow(p1P0.AsInt32(), x0.AsInt32()).AsByte(); + // 02 12 22 32 42 52 62 72 03 13 23 33 43 53 63 73 + p1P0 = Sse2.UnpackHigh(p1P0.AsInt32(), x0.AsInt32()).AsByte(); + p3P2 = Sse2.UnpackHigh(p3P2.AsInt64(), Sse2.ShiftLeftLogical128BitLane(p3P2, 8).AsInt64()) + .AsByte(); // swap lo and high + p1P0 = Sse2.UnpackHigh(p1P0.AsInt64(), Sse2.ShiftLeftLogical128BitLane(p1P0, 8).AsInt64()) + .AsByte(); // swap lo and high + + // 04 14 24 34 05 15 25 35 06 16 26 36 07 17 27 37 + q1Q0 = Sse2.UnpackHigh(q1Q0.AsInt16(), x1.AsInt16()).AsByte(); + // 44 54 64 74 45 55 65 75 46 56 66 76 47 57 67 77 + x2 = Sse2.UnpackHigh(x2.AsInt16(), x3.AsInt16()).AsByte(); + // 06 16 26 36 46 56 66 76 07 17 27 37 47 57 67 77 + q3Q2 = Sse2.UnpackHigh(q1Q0.AsInt32(), x2.AsInt32()).AsByte(); + // 04 14 24 34 44 54 64 74 05 15 25 35 45 55 65 75 + q1Q0 = Sse2.UnpackLow(q1Q0.AsInt32(), x2.AsInt32()).AsByte(); + + q0P0 = Sse2.UnpackLow(p1P0.AsInt64(), q1Q0.AsInt64()).AsByte(); + q1P1 = Sse2.UnpackHigh(p1P0.AsInt64(), q1Q0.AsInt64()).AsByte(); + p1P0 = Sse2.UnpackLow(q0P0.AsInt64(), q1P1.AsInt64()).AsByte(); + p2P1 = Sse2.UnpackLow(q1P1.AsInt64(), p3P2.AsInt64()).AsByte(); + q2Q1 = Sse2.UnpackLow(Sse2.ShiftRightLogical128BitLane(q1P1, 8).AsInt64(), q3Q2.AsInt64()).AsByte(); + + FilterHevMask(q1P1, q0P0, p3P2, p2P1, p1P0, q3Q2, q2Q1, q1Q0, limitV, threshV, out hev, out mask); + Filter4(p1P0, q1Q0, hev, mask, ff, out ps1Ps0, out qs1Qs0); + + // Transpose 8x4 to 4x8 + // qs1qs0: 20 21 22 23 24 25 26 27 30 31 32 33 34 34 36 37 + // ps1ps0: 10 11 12 13 14 15 16 17 00 01 02 03 04 05 06 07 + // 00 01 02 03 04 05 06 07 10 11 12 13 14 15 16 17 + ps1Ps0 = Sse2.UnpackHigh(ps1Ps0.AsInt64(), Sse2.ShiftLeftLogical128BitLane(ps1Ps0, 8).AsInt64()).AsByte(); + // 10 30 11 31 12 32 13 33 14 34 15 35 16 36 17 37 + x0 = Sse2.UnpackHigh(ps1Ps0, qs1Qs0); + // 00 20 01 21 02 22 03 23 04 24 05 25 06 26 07 27 + ps1Ps0 = Sse2.UnpackLow(ps1Ps0, qs1Qs0); + // 04 14 24 34 05 15 25 35 06 16 26 36 07 17 27 37 + qs1Qs0 = Sse2.UnpackHigh(ps1Ps0, x0); + // 00 10 20 30 01 11 21 31 02 12 22 32 03 13 23 33 + ps1Ps0 = Sse2.UnpackLow(ps1Ps0, x0); + + *(int*)(s.ToPointer() + (0 * pitch) - 2) = ps1Ps0.AsInt32().GetElement(0); + ps1Ps0 = Sse2.ShiftRightLogical128BitLane(ps1Ps0, 4); + *(int*)(s.ToPointer() + (1 * pitch) - 2) = ps1Ps0.AsInt32().GetElement(0); + ps1Ps0 = Sse2.ShiftRightLogical128BitLane(ps1Ps0, 4); + *(int*)(s.ToPointer() + (2 * pitch) - 2) = ps1Ps0.AsInt32().GetElement(0); + ps1Ps0 = Sse2.ShiftRightLogical128BitLane(ps1Ps0, 4); + *(int*)(s.ToPointer() + (3 * pitch) - 2) = ps1Ps0.AsInt32().GetElement(0); + + *(int*)(s.ToPointer() + (4 * pitch) - 2) = qs1Qs0.AsInt32().GetElement(0); + qs1Qs0 = Sse2.ShiftRightLogical128BitLane(qs1Qs0, 4); + *(int*)(s.ToPointer() + (5 * pitch) - 2) = qs1Qs0.AsInt32().GetElement(0); + qs1Qs0 = Sse2.ShiftRightLogical128BitLane(qs1Qs0, 4); + *(int*)(s.ToPointer() + (6 * pitch) - 2) = qs1Qs0.AsInt32().GetElement(0); + qs1Qs0 = Sse2.ShiftRightLogical128BitLane(qs1Qs0, 4); + *(int*)(s.ToPointer() + (7 * pitch) - 2) = qs1Qs0.AsInt32().GetElement(0); + } + + public static unsafe void LpfHorizontal16( + ArrayPtr s, + int pitch, + ReadOnlySpan blimit, + ReadOnlySpan limit, + ReadOnlySpan thresh) + { + Vector128 zero = Vector128.Zero; + Vector128 one = Vector128.Create((byte)1); + Vector128 blimitV, limitV, threshV; + + fixed (byte* pBLimit = blimit, pLimit = limit, pThresh = thresh) + { + blimitV = Sse2.LoadVector128(pBLimit); + limitV = Sse2.LoadVector128(pLimit); + threshV = Sse2.LoadVector128(pThresh); + } + + Vector128 mask, hev, flat, flat2; + Vector128 q7P7, q6P6, q5P5, q4P4, q3P3, q2P2, q1P1, q0P0, p0Q0, p1Q1; + Vector128 absP1P0; + + q4P4 = Sse2.LoadScalarVector128((long*)(s.ToPointer() - (5 * pitch))).AsByte(); + q4P4 = Sse.LoadHigh(q4P4.AsSingle(), (float*)(s.ToPointer() + (4 * pitch))).AsByte(); + q3P3 = Sse2.LoadScalarVector128((long*)(s.ToPointer() - (4 * pitch))).AsByte(); + q3P3 = Sse.LoadHigh(q3P3.AsSingle(), (float*)(s.ToPointer() + (3 * pitch))).AsByte(); + q2P2 = Sse2.LoadScalarVector128((long*)(s.ToPointer() - (3 * pitch))).AsByte(); + q2P2 = Sse.LoadHigh(q2P2.AsSingle(), (float*)(s.ToPointer() + (2 * pitch))).AsByte(); + q1P1 = Sse2.LoadScalarVector128((long*)(s.ToPointer() - (2 * pitch))).AsByte(); + q1P1 = Sse.LoadHigh(q1P1.AsSingle(), (float*)(s.ToPointer() + (1 * pitch))).AsByte(); + p1Q1 = Sse2.Shuffle(q1P1.AsUInt32(), 78).AsByte(); + q0P0 = Sse2.LoadScalarVector128((long*)(s.ToPointer() - (1 * pitch))).AsByte(); + q0P0 = Sse.LoadHigh(q0P0.AsSingle(), (float*)(s.ToPointer() - (0 * pitch))).AsByte(); + p0Q0 = Sse2.Shuffle(q0P0.AsUInt32(), 78).AsByte(); + + { + Vector128 absP1Q1, absP0Q0, absQ1Q0, fe, ff, work; + absP1P0 = AbsDiff(q1P1, q0P0); + absQ1Q0 = Sse2.ShiftRightLogical128BitLane(absP1P0, 8); + fe = Vector128.Create((byte)0xfe); + ff = Sse2.CompareEqual(absP1P0, absP1P0); + absP0Q0 = AbsDiff(q0P0, p0Q0); + absP1Q1 = AbsDiff(q1P1, p1Q1); + flat = Sse2.Max(absP1P0, absQ1Q0); + hev = Sse2.SubtractSaturate(flat, threshV); + hev = Sse2.Xor(Sse2.CompareEqual(hev, zero), ff); + + absP0Q0 = Sse2.AddSaturate(absP0Q0, absP0Q0); + absP1Q1 = Sse2.ShiftRightLogical(Sse2.And(absP1Q1, fe).AsInt16(), 1).AsByte(); + mask = Sse2.SubtractSaturate(Sse2.AddSaturate(absP0Q0, absP1Q1), blimitV); + mask = Sse2.Xor(Sse2.CompareEqual(mask, zero), ff); + // mask |= (abs(p0 - q0) * 2 + abs(p1 - q1) / 2 > blimit) * -1; + mask = Sse2.Max(absP1P0, mask); + // mask |= (abs(p1 - p0) > limit) * -1; + // mask |= (abs(q1 - q0) > limit) * -1; + + work = Sse2.Max(AbsDiff(q2P2, q1P1), AbsDiff(q3P3, q2P2)); + mask = Sse2.Max(work, mask); + mask = Sse2.Max(mask, Sse2.ShiftRightLogical128BitLane(mask, 8)); + mask = Sse2.SubtractSaturate(mask, limitV); + mask = Sse2.CompareEqual(mask, zero); + } + + // lp filter + { + Vector128 t4 = Vector128.Create((byte)4); + Vector128 t3 = Vector128.Create((byte)3); + Vector128 t80 = Vector128.Create((byte)0x80); + Vector128 t1 = Vector128.Create((ushort)0x1); + Vector128 qs1Ps1 = Sse2.Xor(q1P1, t80); + Vector128 qs0Ps0 = Sse2.Xor(q0P0, t80); + Vector128 qs0 = Sse2.Xor(p0Q0, t80); + Vector128 qs1 = Sse2.Xor(p1Q1, t80); + Vector128 filt; + Vector128 workA; + Vector128 filter1, filter2; + Vector128 flat2Q6P6, flat2Q5P5, flat2Q4P4, flat2Q3P3, flat2Q2P2; + Vector128 flat2Q1P1, flat2Q0P0, flatQ2P2, flatQ1P1, flatQ0P0; + + filt = Sse2.And(Sse2.SubtractSaturate(qs1Ps1.AsSByte(), qs1.AsSByte()).AsByte(), hev); + workA = Sse2.SubtractSaturate(qs0.AsSByte(), qs0Ps0.AsSByte()).AsByte(); + filt = Sse2.AddSaturate(filt.AsSByte(), workA.AsSByte()).AsByte(); + filt = Sse2.AddSaturate(filt.AsSByte(), workA.AsSByte()).AsByte(); + filt = Sse2.AddSaturate(filt.AsSByte(), workA.AsSByte()).AsByte(); + // (vpx_filter + 3 * (qs0 - ps0)) & mask + filt = Sse2.And(filt, mask); + + filter1 = Sse2.AddSaturate(filt.AsSByte(), t4.AsSByte()).AsByte(); + filter2 = Sse2.AddSaturate(filt.AsSByte(), t3.AsSByte()).AsByte(); + + filter1 = Sse2.UnpackLow(zero, filter1); + filter1 = Sse2.ShiftRightArithmetic(filter1.AsInt16(), 0xB).AsByte(); + filter2 = Sse2.UnpackLow(zero, filter2); + filter2 = Sse2.ShiftRightArithmetic(filter2.AsInt16(), 0xB).AsByte(); + + // Filter1 >> 3 + filt = Sse2.PackSignedSaturate(filter2.AsInt16(), + Sse2.SubtractSaturate(zero.AsInt16(), filter1.AsInt16())).AsByte(); + qs0Ps0 = Sse2.Xor(Sse2.AddSaturate(qs0Ps0.AsSByte(), filt.AsSByte()).AsByte(), t80); + + // filt >> 1 + filt = Sse2.AddSaturate(filter1.AsInt16(), t1.AsInt16()).AsByte(); + filt = Sse2.ShiftRightArithmetic(filt.AsInt16(), 1).AsByte(); + filt = Sse2.AndNot(Sse2.ShiftRightArithmetic(Sse2.UnpackLow(zero, hev).AsInt16(), 0x8), filt.AsInt16()) + .AsByte(); + filt = Sse2.PackSignedSaturate(filt.AsInt16(), Sse2.SubtractSaturate(zero.AsInt16(), filt.AsInt16())) + .AsByte(); + qs1Ps1 = Sse2.Xor(Sse2.AddSaturate(qs1Ps1.AsSByte(), filt.AsSByte()).AsByte(), t80); + // loopfilter done + + { + Vector128 work; + flat = Sse2.Max(AbsDiff(q2P2, q0P0), AbsDiff(q3P3, q0P0)); + flat = Sse2.Max(absP1P0, flat); + flat = Sse2.Max(flat, Sse2.ShiftRightLogical128BitLane(flat, 8)); + flat = Sse2.SubtractSaturate(flat, one); + flat = Sse2.CompareEqual(flat, zero); + flat = Sse2.And(flat, mask); + + q5P5 = Sse2.LoadScalarVector128((long*)(s.ToPointer() - (6 * pitch))).AsByte(); + q5P5 = Sse.LoadHigh(q5P5.AsSingle(), (float*)(s.ToPointer() + (5 * pitch))).AsByte(); + + q6P6 = Sse2.LoadScalarVector128((long*)(s.ToPointer() - (7 * pitch))).AsByte(); + q6P6 = Sse.LoadHigh(q6P6.AsSingle(), (float*)(s.ToPointer() + (6 * pitch))).AsByte(); + flat2 = Sse2.Max(AbsDiff(q4P4, q0P0), AbsDiff(q5P5, q0P0)); + + q7P7 = Sse2.LoadScalarVector128((long*)(s.ToPointer() - (8 * pitch))).AsByte(); + q7P7 = Sse.LoadHigh(q7P7.AsSingle(), (float*)(s.ToPointer() + (7 * pitch))).AsByte(); + work = Sse2.Max(AbsDiff(q6P6, q0P0), AbsDiff(q7P7, q0P0)); + flat2 = Sse2.Max(work, flat2); + flat2 = Sse2.Max(flat2, Sse2.ShiftRightLogical128BitLane(flat2, 8)); + flat2 = Sse2.SubtractSaturate(flat2, one); + flat2 = Sse2.CompareEqual(flat2, zero); + flat2 = Sse2.And(flat2, flat); // flat2 & flat & mask + } + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // flat and wide flat calculations + { + Vector128 eight = Vector128.Create((short)8); + Vector128 four = Vector128.Create((short)4); + Vector128 p716, p616, p516, p416, p316, p216, p116, p016; + Vector128 q716, q616, q516, q416, q316, q216, q116, q016; + Vector128 pixelFilterP, pixelFilterQ; + Vector128 pixetFilterP2P1P0, pixetFilterQ2Q1Q0; + Vector128 sumP7, sumQ7, sumP3, sumQ3, resP, resQ; + + p716 = Sse2.UnpackLow(q7P7, zero).AsInt16(); + p616 = Sse2.UnpackLow(q6P6, zero).AsInt16(); + p516 = Sse2.UnpackLow(q5P5, zero).AsInt16(); + p416 = Sse2.UnpackLow(q4P4, zero).AsInt16(); + p316 = Sse2.UnpackLow(q3P3, zero).AsInt16(); + p216 = Sse2.UnpackLow(q2P2, zero).AsInt16(); + p116 = Sse2.UnpackLow(q1P1, zero).AsInt16(); + p016 = Sse2.UnpackLow(q0P0, zero).AsInt16(); + q016 = Sse2.UnpackHigh(q0P0, zero).AsInt16(); + q116 = Sse2.UnpackHigh(q1P1, zero).AsInt16(); + q216 = Sse2.UnpackHigh(q2P2, zero).AsInt16(); + q316 = Sse2.UnpackHigh(q3P3, zero).AsInt16(); + q416 = Sse2.UnpackHigh(q4P4, zero).AsInt16(); + q516 = Sse2.UnpackHigh(q5P5, zero).AsInt16(); + q616 = Sse2.UnpackHigh(q6P6, zero).AsInt16(); + q716 = Sse2.UnpackHigh(q7P7, zero).AsInt16(); + + pixelFilterP = Sse2.Add(Sse2.Add(p616, p516), Sse2.Add(p416, p316)); + pixelFilterQ = Sse2.Add(Sse2.Add(q616, q516), Sse2.Add(q416, q316)); + + pixetFilterP2P1P0 = Sse2.Add(p016, Sse2.Add(p216, p116)); + pixelFilterP = Sse2.Add(pixelFilterP, pixetFilterP2P1P0); + + pixetFilterQ2Q1Q0 = Sse2.Add(q016, Sse2.Add(q216, q116)); + pixelFilterQ = Sse2.Add(pixelFilterQ, pixetFilterQ2Q1Q0); + pixelFilterP = Sse2.Add(eight, Sse2.Add(pixelFilterP, pixelFilterQ)); + pixetFilterP2P1P0 = Sse2.Add(four, Sse2.Add(pixetFilterP2P1P0, pixetFilterQ2Q1Q0)); + resP = Sse2.ShiftRightLogical(Sse2.Add(pixelFilterP, Sse2.Add(p716, p016)), 4); + resQ = Sse2.ShiftRightLogical(Sse2.Add(pixelFilterP, Sse2.Add(q716, q016)), 4); + flat2Q0P0 = Sse2.PackUnsignedSaturate(resP, resQ); + resP = Sse2.ShiftRightLogical(Sse2.Add(pixetFilterP2P1P0, Sse2.Add(p316, p016)), 3); + resQ = Sse2.ShiftRightLogical(Sse2.Add(pixetFilterP2P1P0, Sse2.Add(q316, q016)), 3); + + flatQ0P0 = Sse2.PackUnsignedSaturate(resP, resQ); + + sumP7 = Sse2.Add(p716, p716); + sumQ7 = Sse2.Add(q716, q716); + sumP3 = Sse2.Add(p316, p316); + sumQ3 = Sse2.Add(q316, q316); + + pixelFilterQ = Sse2.Subtract(pixelFilterP, p616); + pixelFilterP = Sse2.Subtract(pixelFilterP, q616); + resP = Sse2.ShiftRightLogical(Sse2.Add(pixelFilterP, Sse2.Add(sumP7, p116)), 4); + resQ = Sse2.ShiftRightLogical(Sse2.Add(pixelFilterQ, Sse2.Add(sumQ7, q116)), 4); + flat2Q1P1 = Sse2.PackUnsignedSaturate(resP, resQ); + + pixetFilterQ2Q1Q0 = Sse2.Subtract(pixetFilterP2P1P0, p216); + pixetFilterP2P1P0 = Sse2.Subtract(pixetFilterP2P1P0, q216); + resP = Sse2.ShiftRightLogical(Sse2.Add(pixetFilterP2P1P0, Sse2.Add(sumP3, p116)), 3); + resQ = Sse2.ShiftRightLogical(Sse2.Add(pixetFilterQ2Q1Q0, Sse2.Add(sumQ3, q116)), 3); + flatQ1P1 = Sse2.PackUnsignedSaturate(resP, resQ); + + sumP7 = Sse2.Add(sumP7, p716); + sumQ7 = Sse2.Add(sumQ7, q716); + sumP3 = Sse2.Add(sumP3, p316); + sumQ3 = Sse2.Add(sumQ3, q316); + + pixelFilterP = Sse2.Subtract(pixelFilterP, q516); + pixelFilterQ = Sse2.Subtract(pixelFilterQ, p516); + resP = Sse2.ShiftRightLogical(Sse2.Add(pixelFilterP, Sse2.Add(sumP7, p216)), 4); + resQ = Sse2.ShiftRightLogical(Sse2.Add(pixelFilterQ, Sse2.Add(sumQ7, q216)), 4); + flat2Q2P2 = Sse2.PackUnsignedSaturate(resP, resQ); + + pixetFilterP2P1P0 = Sse2.Subtract(pixetFilterP2P1P0, q116); + pixetFilterQ2Q1Q0 = Sse2.Subtract(pixetFilterQ2Q1Q0, p116); + + resP = Sse2.ShiftRightLogical(Sse2.Add(pixetFilterP2P1P0, Sse2.Add(sumP3, p216)), 3); + resQ = Sse2.ShiftRightLogical(Sse2.Add(pixetFilterQ2Q1Q0, Sse2.Add(sumQ3, q216)), 3); + flatQ2P2 = Sse2.PackUnsignedSaturate(resP, resQ); + + sumP7 = Sse2.Add(sumP7, p716); + sumQ7 = Sse2.Add(sumQ7, q716); + pixelFilterP = Sse2.Subtract(pixelFilterP, q416); + pixelFilterQ = Sse2.Subtract(pixelFilterQ, p416); + resP = Sse2.ShiftRightLogical(Sse2.Add(pixelFilterP, Sse2.Add(sumP7, p316)), 4); + resQ = Sse2.ShiftRightLogical(Sse2.Add(pixelFilterQ, Sse2.Add(sumQ7, q316)), 4); + flat2Q3P3 = Sse2.PackUnsignedSaturate(resP, resQ); + + sumP7 = Sse2.Add(sumP7, p716); + sumQ7 = Sse2.Add(sumQ7, q716); + pixelFilterP = Sse2.Subtract(pixelFilterP, q316); + pixelFilterQ = Sse2.Subtract(pixelFilterQ, p316); + resP = Sse2.ShiftRightLogical(Sse2.Add(pixelFilterP, Sse2.Add(sumP7, p416)), 4); + resQ = Sse2.ShiftRightLogical(Sse2.Add(pixelFilterQ, Sse2.Add(sumQ7, q416)), 4); + flat2Q4P4 = Sse2.PackUnsignedSaturate(resP, resQ); + + sumP7 = Sse2.Add(sumP7, p716); + sumQ7 = Sse2.Add(sumQ7, q716); + pixelFilterP = Sse2.Subtract(pixelFilterP, q216); + pixelFilterQ = Sse2.Subtract(pixelFilterQ, p216); + resP = Sse2.ShiftRightLogical(Sse2.Add(pixelFilterP, Sse2.Add(sumP7, p516)), 4); + resQ = Sse2.ShiftRightLogical(Sse2.Add(pixelFilterQ, Sse2.Add(sumQ7, q516)), 4); + flat2Q5P5 = Sse2.PackUnsignedSaturate(resP, resQ); + + sumP7 = Sse2.Add(sumP7, p716); + sumQ7 = Sse2.Add(sumQ7, q716); + pixelFilterP = Sse2.Subtract(pixelFilterP, q116); + pixelFilterQ = Sse2.Subtract(pixelFilterQ, p116); + resP = Sse2.ShiftRightLogical(Sse2.Add(pixelFilterP, Sse2.Add(sumP7, p616)), 4); + resQ = Sse2.ShiftRightLogical(Sse2.Add(pixelFilterQ, Sse2.Add(sumQ7, q616)), 4); + flat2Q6P6 = Sse2.PackUnsignedSaturate(resP, resQ); + } + // wide flat + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + flat = Sse2.Shuffle(flat.AsInt32(), 68).AsByte(); + flat2 = Sse2.Shuffle(flat2.AsInt32(), 68).AsByte(); + + q2P2 = Sse2.AndNot(flat, q2P2); + flatQ2P2 = Sse2.And(flat, flatQ2P2); + q2P2 = Sse2.Or(q2P2, flatQ2P2); + + qs1Ps1 = Sse2.AndNot(flat, qs1Ps1); + flatQ1P1 = Sse2.And(flat, flatQ1P1); + q1P1 = Sse2.Or(qs1Ps1, flatQ1P1); + + qs0Ps0 = Sse2.AndNot(flat, qs0Ps0); + flatQ0P0 = Sse2.And(flat, flatQ0P0); + q0P0 = Sse2.Or(qs0Ps0, flatQ0P0); + + q6P6 = Sse2.AndNot(flat2, q6P6); + flat2Q6P6 = Sse2.And(flat2, flat2Q6P6); + q6P6 = Sse2.Or(q6P6, flat2Q6P6); + Sse2.StoreScalar((long*)(s.ToPointer() - (7 * pitch)), q6P6.AsInt64()); + Sse.StoreHigh((float*)(s.ToPointer() + (6 * pitch)), q6P6.AsSingle()); + + q5P5 = Sse2.AndNot(flat2, q5P5); + flat2Q5P5 = Sse2.And(flat2, flat2Q5P5); + q5P5 = Sse2.Or(q5P5, flat2Q5P5); + Sse2.StoreScalar((long*)(s.ToPointer() - (6 * pitch)), q5P5.AsInt64()); + Sse.StoreHigh((float*)(s.ToPointer() + (5 * pitch)), q5P5.AsSingle()); + + q4P4 = Sse2.AndNot(flat2, q4P4); + flat2Q4P4 = Sse2.And(flat2, flat2Q4P4); + q4P4 = Sse2.Or(q4P4, flat2Q4P4); + Sse2.StoreScalar((long*)(s.ToPointer() - (5 * pitch)), q4P4.AsInt64()); + Sse.StoreHigh((float*)(s.ToPointer() + (4 * pitch)), q4P4.AsSingle()); + + q3P3 = Sse2.AndNot(flat2, q3P3); + flat2Q3P3 = Sse2.And(flat2, flat2Q3P3); + q3P3 = Sse2.Or(q3P3, flat2Q3P3); + Sse2.StoreScalar((long*)(s.ToPointer() - (4 * pitch)), q3P3.AsInt64()); + Sse.StoreHigh((float*)(s.ToPointer() + (3 * pitch)), q3P3.AsSingle()); + + q2P2 = Sse2.AndNot(flat2, q2P2); + flat2Q2P2 = Sse2.And(flat2, flat2Q2P2); + q2P2 = Sse2.Or(q2P2, flat2Q2P2); + Sse2.StoreScalar((long*)(s.ToPointer() - (3 * pitch)), q2P2.AsInt64()); + Sse.StoreHigh((float*)(s.ToPointer() + (2 * pitch)), q2P2.AsSingle()); + + q1P1 = Sse2.AndNot(flat2, q1P1); + flat2Q1P1 = Sse2.And(flat2, flat2Q1P1); + q1P1 = Sse2.Or(q1P1, flat2Q1P1); + Sse2.StoreScalar((long*)(s.ToPointer() - (2 * pitch)), q1P1.AsInt64()); + Sse.StoreHigh((float*)(s.ToPointer() + (1 * pitch)), q1P1.AsSingle()); + + q0P0 = Sse2.AndNot(flat2, q0P0); + flat2Q0P0 = Sse2.And(flat2, flat2Q0P0); + q0P0 = Sse2.Or(q0P0, flat2Q0P0); + Sse2.StoreScalar((long*)(s.ToPointer() - (1 * pitch)), q0P0.AsInt64()); + Sse.StoreHigh((float*)(s.ToPointer() - (0 * pitch)), q0P0.AsSingle()); + } + } + + private static Vector128 FilterAdd2Sub2( + Vector128 total, + Vector128 a1, + Vector128 a2, + Vector128 s1, + Vector128 s2) + { + Vector128 x = Sse2.Add(a1, total); + x = Sse2.Add(Sse2.Subtract(x, Sse2.Add(s1, s2)), a2); + return x; + } + + private static Vector128 Filter8Mask( + Vector128 flat, + Vector128 otherFilt, + Vector128 f8Lo, + Vector128 f8Hi) + { + Vector128 f8 = + Sse2.PackUnsignedSaturate(Sse2.ShiftRightLogical(f8Lo, 3), Sse2.ShiftRightLogical(f8Hi, 3)); + Vector128 result = Sse2.And(flat, f8); + return Sse2.Or(Sse2.AndNot(flat, otherFilt), result); + } + + private static Vector128 Filter16Mask( + Vector128 flat, + Vector128 otherFilt, + Vector128 fLo, + Vector128 fHi) + { + Vector128 f = + Sse2.PackUnsignedSaturate(Sse2.ShiftRightLogical(fLo, 4), Sse2.ShiftRightLogical(fHi, 4)); + Vector128 result = Sse2.And(flat, f); + return Sse2.Or(Sse2.AndNot(flat, otherFilt), result); + } + + public static unsafe void LpfHorizontal16Dual( + ArrayPtr s, + int pitch, + ReadOnlySpan blimit, + ReadOnlySpan limit, + ReadOnlySpan thresh) + { + Vector128 zero = Vector128.Zero; + Vector128 one = Vector128.Create((byte)1); + Vector128 blimitV, limitV, threshV; + + fixed (byte* pBLimit = blimit, pLimit = limit, pThresh = thresh) + { + blimitV = Sse2.LoadVector128(pBLimit); + limitV = Sse2.LoadVector128(pLimit); + threshV = Sse2.LoadVector128(pThresh); + } + + Vector128 mask, hev, flat, flat2; + Vector128 p7, p6, p5; + Vector128 p4, p3, p2, p1, p0, q0, q1, q2, q3, q4; + Vector128 q5, q6, q7; + + Vector128 op2, op1, op0, oq0, oq1, oq2; + + Vector128 maxAbsP1P0Q1Q0; + + p7 = Sse2.LoadVector128(s.ToPointer() - (8 * pitch)); + p6 = Sse2.LoadVector128(s.ToPointer() - (7 * pitch)); + p5 = Sse2.LoadVector128(s.ToPointer() - (6 * pitch)); + p4 = Sse2.LoadVector128(s.ToPointer() - (5 * pitch)); + p3 = Sse2.LoadVector128(s.ToPointer() - (4 * pitch)); + p2 = Sse2.LoadVector128(s.ToPointer() - (3 * pitch)); + p1 = Sse2.LoadVector128(s.ToPointer() - (2 * pitch)); + p0 = Sse2.LoadVector128(s.ToPointer() - (1 * pitch)); + q0 = Sse2.LoadVector128(s.ToPointer() - (0 * pitch)); + q1 = Sse2.LoadVector128(s.ToPointer() + (1 * pitch)); + q2 = Sse2.LoadVector128(s.ToPointer() + (2 * pitch)); + q3 = Sse2.LoadVector128(s.ToPointer() + (3 * pitch)); + q4 = Sse2.LoadVector128(s.ToPointer() + (4 * pitch)); + q5 = Sse2.LoadVector128(s.ToPointer() + (5 * pitch)); + q6 = Sse2.LoadVector128(s.ToPointer() + (6 * pitch)); + q7 = Sse2.LoadVector128(s.ToPointer() + (7 * pitch)); + + { + Vector128 absP1P0 = AbsDiff(p1, p0); + Vector128 absQ1Q0 = AbsDiff(q1, q0); + Vector128 fe = Vector128.Create((byte)0xfe); + Vector128 ff = Sse2.CompareEqual(zero, zero); + Vector128 absP0Q0 = AbsDiff(p0, q0); + Vector128 absP1Q1 = AbsDiff(p1, q1); + Vector128 work; + maxAbsP1P0Q1Q0 = Sse2.Max(absP1P0, absQ1Q0); + + absP0Q0 = Sse2.AddSaturate(absP0Q0, absP0Q0); + absP1Q1 = Sse2.ShiftRightLogical(Sse2.And(absP1Q1, fe).AsInt16(), 1).AsByte(); + mask = Sse2.SubtractSaturate(Sse2.AddSaturate(absP0Q0, absP1Q1), blimitV); + mask = Sse2.Xor(Sse2.CompareEqual(mask, zero), ff); + // mask |= (abs(p0 - q0) * 2 + abs(p1 - q1) / 2 > blimit) * -1; + mask = Sse2.Max(maxAbsP1P0Q1Q0, mask); + // mask |= (abs(p1 - p0) > limit) * -1; + // mask |= (abs(q1 - q0) > limit) * -1; + work = Sse2.Max(AbsDiff(p2, p1), AbsDiff(p3, p2)); + mask = Sse2.Max(work, mask); + work = Sse2.Max(AbsDiff(q2, q1), AbsDiff(q3, q2)); + mask = Sse2.Max(work, mask); + mask = Sse2.SubtractSaturate(mask, limitV); + mask = Sse2.CompareEqual(mask, zero); + } + + { + Vector128 work; + work = Sse2.Max(AbsDiff(p2, p0), AbsDiff(q2, q0)); + flat = Sse2.Max(work, maxAbsP1P0Q1Q0); + work = Sse2.Max(AbsDiff(p3, p0), AbsDiff(q3, q0)); + flat = Sse2.Max(work, flat); + work = Sse2.Max(AbsDiff(p4, p0), AbsDiff(q4, q0)); + flat = Sse2.SubtractSaturate(flat, one); + flat = Sse2.CompareEqual(flat, zero); + flat = Sse2.And(flat, mask); + flat2 = Sse2.Max(AbsDiff(p5, p0), AbsDiff(q5, q0)); + flat2 = Sse2.Max(work, flat2); + work = Sse2.Max(AbsDiff(p6, p0), AbsDiff(q6, q0)); + flat2 = Sse2.Max(work, flat2); + work = Sse2.Max(AbsDiff(p7, p0), AbsDiff(q7, q0)); + flat2 = Sse2.Max(work, flat2); + flat2 = Sse2.SubtractSaturate(flat2, one); + flat2 = Sse2.CompareEqual(flat2, zero); + flat2 = Sse2.And(flat2, flat); // flat2 & flat & mask + } + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // filter4 + { + Vector128 t4 = Vector128.Create((byte)4); + Vector128 t3 = Vector128.Create((byte)3); + Vector128 t80 = Vector128.Create((byte)0x80); + Vector128 te0 = Vector128.Create((byte)0xe0); + Vector128 t1F = Vector128.Create((byte)0x1f); + Vector128 t1 = Vector128.Create((byte)0x1); + Vector128 t7F = Vector128.Create((byte)0x7f); + Vector128 ff = Sse2.CompareEqual(t4, t4); + + Vector128 filt; + Vector128 workA; + Vector128 filter1, filter2; + + op1 = Sse2.Xor(p1, t80); + op0 = Sse2.Xor(p0, t80); + oq0 = Sse2.Xor(q0, t80); + oq1 = Sse2.Xor(q1, t80); + + hev = Sse2.SubtractSaturate(maxAbsP1P0Q1Q0, threshV); + hev = Sse2.Xor(Sse2.CompareEqual(hev, zero), ff); + filt = Sse2.And(Sse2.SubtractSaturate(op1.AsSByte(), oq1.AsSByte()).AsByte(), hev); + + workA = Sse2.SubtractSaturate(oq0.AsSByte(), op0.AsSByte()).AsByte(); + filt = Sse2.AddSaturate(filt.AsSByte(), workA.AsSByte()).AsByte(); + filt = Sse2.AddSaturate(filt.AsSByte(), workA.AsSByte()).AsByte(); + filt = Sse2.AddSaturate(filt.AsSByte(), workA.AsSByte()).AsByte(); + // (vpx_filter + 3 * (qs0 - ps0)) & mask + filt = Sse2.And(filt, mask); + filter1 = Sse2.AddSaturate(filt.AsSByte(), t4.AsSByte()).AsByte(); + filter2 = Sse2.AddSaturate(filt.AsSByte(), t3.AsSByte()).AsByte(); + + // Filter1 >> 3 + workA = Sse2.CompareGreaterThan(zero.AsSByte(), filter1.AsSByte()).AsByte(); + filter1 = Sse2.ShiftRightLogical(filter1.AsInt16(), 3).AsByte(); + workA = Sse2.And(workA, te0); + filter1 = Sse2.And(filter1, t1F); + filter1 = Sse2.Or(filter1, workA); + oq0 = Sse2.Xor(Sse2.SubtractSaturate(oq0.AsSByte(), filter1.AsSByte()).AsByte(), t80); + + // Filter2 >> 3 + workA = Sse2.CompareGreaterThan(zero.AsSByte(), filter2.AsSByte()).AsByte(); + filter2 = Sse2.ShiftRightLogical(filter2.AsInt16(), 3).AsByte(); + workA = Sse2.And(workA, te0); + filter2 = Sse2.And(filter2, t1F); + filter2 = Sse2.Or(filter2, workA); + op0 = Sse2.Xor(Sse2.AddSaturate(op0.AsSByte(), filter2.AsSByte()).AsByte(), t80); + + // filt >> 1 + filt = Sse2.AddSaturate(filter1.AsSByte(), t1.AsSByte()).AsByte(); + workA = Sse2.CompareGreaterThan(zero.AsSByte(), filt.AsSByte()).AsByte(); + filt = Sse2.ShiftRightLogical(filt.AsInt16(), 1).AsByte(); + workA = Sse2.And(workA, t80); + filt = Sse2.And(filt, t7F); + filt = Sse2.Or(filt, workA); + filt = Sse2.AndNot(hev, filt); + op1 = Sse2.Xor(Sse2.AddSaturate(op1.AsSByte(), filt.AsSByte()).AsByte(), t80); + oq1 = Sse2.Xor(Sse2.SubtractSaturate(oq1.AsSByte(), filt.AsSByte()).AsByte(), t80); + // loopfilter done + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // filter8 + { + Vector128 four = Vector128.Create((short)4); + Vector128 p3Lo = Sse2.UnpackLow(p3, zero).AsInt16(); + Vector128 p2Lo = Sse2.UnpackLow(p2, zero).AsInt16(); + Vector128 p1Lo = Sse2.UnpackLow(p1, zero).AsInt16(); + Vector128 p0Lo = Sse2.UnpackLow(p0, zero).AsInt16(); + Vector128 q0Lo = Sse2.UnpackLow(q0, zero).AsInt16(); + Vector128 q1Lo = Sse2.UnpackLow(q1, zero).AsInt16(); + Vector128 q2Lo = Sse2.UnpackLow(q2, zero).AsInt16(); + Vector128 q3Lo = Sse2.UnpackLow(q3, zero).AsInt16(); + + Vector128 p3Hi = Sse2.UnpackHigh(p3, zero).AsInt16(); + Vector128 p2Hi = Sse2.UnpackHigh(p2, zero).AsInt16(); + Vector128 p1Hi = Sse2.UnpackHigh(p1, zero).AsInt16(); + Vector128 p0Hi = Sse2.UnpackHigh(p0, zero).AsInt16(); + Vector128 q0Hi = Sse2.UnpackHigh(q0, zero).AsInt16(); + Vector128 q1Hi = Sse2.UnpackHigh(q1, zero).AsInt16(); + Vector128 q2Hi = Sse2.UnpackHigh(q2, zero).AsInt16(); + Vector128 q3Hi = Sse2.UnpackHigh(q3, zero).AsInt16(); + Vector128 f8Lo, f8Hi; + + f8Lo = Sse2.Add(Sse2.Add(p3Lo, four), Sse2.Add(p3Lo, p2Lo)); + f8Lo = Sse2.Add(Sse2.Add(p3Lo, f8Lo), Sse2.Add(p2Lo, p1Lo)); + f8Lo = Sse2.Add(Sse2.Add(p0Lo, q0Lo), f8Lo); + + f8Hi = Sse2.Add(Sse2.Add(p3Hi, four), Sse2.Add(p3Hi, p2Hi)); + f8Hi = Sse2.Add(Sse2.Add(p3Hi, f8Hi), Sse2.Add(p2Hi, p1Hi)); + f8Hi = Sse2.Add(Sse2.Add(p0Hi, q0Hi), f8Hi); + + op2 = Filter8Mask(flat, p2, f8Lo, f8Hi); + + f8Lo = FilterAdd2Sub2(f8Lo, q1Lo, p1Lo, p2Lo, p3Lo); + f8Hi = FilterAdd2Sub2(f8Hi, q1Hi, p1Hi, p2Hi, p3Hi); + op1 = Filter8Mask(flat, op1, f8Lo, f8Hi); + + f8Lo = FilterAdd2Sub2(f8Lo, q2Lo, p0Lo, p1Lo, p3Lo); + f8Hi = FilterAdd2Sub2(f8Hi, q2Hi, p0Hi, p1Hi, p3Hi); + op0 = Filter8Mask(flat, op0, f8Lo, f8Hi); + + f8Lo = FilterAdd2Sub2(f8Lo, q3Lo, q0Lo, p0Lo, p3Lo); + f8Hi = FilterAdd2Sub2(f8Hi, q3Hi, q0Hi, p0Hi, p3Hi); + oq0 = Filter8Mask(flat, oq0, f8Lo, f8Hi); + + f8Lo = FilterAdd2Sub2(f8Lo, q3Lo, q1Lo, q0Lo, p2Lo); + f8Hi = FilterAdd2Sub2(f8Hi, q3Hi, q1Hi, q0Hi, p2Hi); + oq1 = Filter8Mask(flat, oq1, f8Lo, f8Hi); + + f8Lo = FilterAdd2Sub2(f8Lo, q3Lo, q2Lo, q1Lo, p1Lo); + f8Hi = FilterAdd2Sub2(f8Hi, q3Hi, q2Hi, q1Hi, p1Hi); + oq2 = Filter8Mask(flat, q2, f8Lo, f8Hi); + } + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // wide flat calculations + { + Vector128 eight = Vector128.Create((short)8); + Vector128 p7Lo = Sse2.UnpackLow(p7, zero).AsInt16(); + Vector128 p6Lo = Sse2.UnpackLow(p6, zero).AsInt16(); + Vector128 p5Lo = Sse2.UnpackLow(p5, zero).AsInt16(); + Vector128 p4Lo = Sse2.UnpackLow(p4, zero).AsInt16(); + Vector128 p3Lo = Sse2.UnpackLow(p3, zero).AsInt16(); + Vector128 p2Lo = Sse2.UnpackLow(p2, zero).AsInt16(); + Vector128 p1Lo = Sse2.UnpackLow(p1, zero).AsInt16(); + Vector128 p0Lo = Sse2.UnpackLow(p0, zero).AsInt16(); + Vector128 q0Lo = Sse2.UnpackLow(q0, zero).AsInt16(); + Vector128 q1Lo = Sse2.UnpackLow(q1, zero).AsInt16(); + Vector128 q2Lo = Sse2.UnpackLow(q2, zero).AsInt16(); + Vector128 q3Lo = Sse2.UnpackLow(q3, zero).AsInt16(); + Vector128 q4Lo = Sse2.UnpackLow(q4, zero).AsInt16(); + Vector128 q5Lo = Sse2.UnpackLow(q5, zero).AsInt16(); + Vector128 q6Lo = Sse2.UnpackLow(q6, zero).AsInt16(); + Vector128 q7Lo = Sse2.UnpackLow(q7, zero).AsInt16(); + + Vector128 p7Hi = Sse2.UnpackHigh(p7, zero).AsInt16(); + Vector128 p6Hi = Sse2.UnpackHigh(p6, zero).AsInt16(); + Vector128 p5Hi = Sse2.UnpackHigh(p5, zero).AsInt16(); + Vector128 p4Hi = Sse2.UnpackHigh(p4, zero).AsInt16(); + Vector128 p3Hi = Sse2.UnpackHigh(p3, zero).AsInt16(); + Vector128 p2Hi = Sse2.UnpackHigh(p2, zero).AsInt16(); + Vector128 p1Hi = Sse2.UnpackHigh(p1, zero).AsInt16(); + Vector128 p0Hi = Sse2.UnpackHigh(p0, zero).AsInt16(); + Vector128 q0Hi = Sse2.UnpackHigh(q0, zero).AsInt16(); + Vector128 q1Hi = Sse2.UnpackHigh(q1, zero).AsInt16(); + Vector128 q2Hi = Sse2.UnpackHigh(q2, zero).AsInt16(); + Vector128 q3Hi = Sse2.UnpackHigh(q3, zero).AsInt16(); + Vector128 q4Hi = Sse2.UnpackHigh(q4, zero).AsInt16(); + Vector128 q5Hi = Sse2.UnpackHigh(q5, zero).AsInt16(); + Vector128 q6Hi = Sse2.UnpackHigh(q6, zero).AsInt16(); + Vector128 q7Hi = Sse2.UnpackHigh(q7, zero).AsInt16(); + + Vector128 fLo; + Vector128 fHi; + + fLo = Sse2.Subtract(Sse2.ShiftLeftLogical(p7Lo, 3), p7Lo); // p7 * 7 + fLo = Sse2.Add(Sse2.ShiftLeftLogical(p6Lo, 1), Sse2.Add(p4Lo, fLo)); + fLo = Sse2.Add(Sse2.Add(p3Lo, fLo), Sse2.Add(p2Lo, p1Lo)); + fLo = Sse2.Add(Sse2.Add(p0Lo, q0Lo), fLo); + fLo = Sse2.Add(Sse2.Add(p5Lo, eight), fLo); + + fHi = Sse2.Subtract(Sse2.ShiftLeftLogical(p7Hi, 3), p7Hi); // p7 * 7 + fHi = Sse2.Add(Sse2.ShiftLeftLogical(p6Hi, 1), Sse2.Add(p4Hi, fHi)); + fHi = Sse2.Add(Sse2.Add(p3Hi, fHi), Sse2.Add(p2Hi, p1Hi)); + fHi = Sse2.Add(Sse2.Add(p0Hi, q0Hi), fHi); + fHi = Sse2.Add(Sse2.Add(p5Hi, eight), fHi); + + p6 = Filter16Mask(flat2, p6, fLo, fHi); + Sse2.Store(s.ToPointer() - (7 * pitch), p6); + + fLo = FilterAdd2Sub2(fLo, q1Lo, p5Lo, p6Lo, p7Lo); + fHi = FilterAdd2Sub2(fHi, q1Hi, p5Hi, p6Hi, p7Hi); + p5 = Filter16Mask(flat2, p5, fLo, fHi); + Sse2.Store(s.ToPointer() - (6 * pitch), p5); + + fLo = FilterAdd2Sub2(fLo, q2Lo, p4Lo, p5Lo, p7Lo); + fHi = FilterAdd2Sub2(fHi, q2Hi, p4Hi, p5Hi, p7Hi); + p4 = Filter16Mask(flat2, p4, fLo, fHi); + Sse2.Store(s.ToPointer() - (5 * pitch), p4); + + fLo = FilterAdd2Sub2(fLo, q3Lo, p3Lo, p4Lo, p7Lo); + fHi = FilterAdd2Sub2(fHi, q3Hi, p3Hi, p4Hi, p7Hi); + p3 = Filter16Mask(flat2, p3, fLo, fHi); + Sse2.Store(s.ToPointer() - (4 * pitch), p3); + + fLo = FilterAdd2Sub2(fLo, q4Lo, p2Lo, p3Lo, p7Lo); + fHi = FilterAdd2Sub2(fHi, q4Hi, p2Hi, p3Hi, p7Hi); + op2 = Filter16Mask(flat2, op2, fLo, fHi); + Sse2.Store(s.ToPointer() - (3 * pitch), op2); + + fLo = FilterAdd2Sub2(fLo, q5Lo, p1Lo, p2Lo, p7Lo); + fHi = FilterAdd2Sub2(fHi, q5Hi, p1Hi, p2Hi, p7Hi); + op1 = Filter16Mask(flat2, op1, fLo, fHi); + Sse2.Store(s.ToPointer() - (2 * pitch), op1); + + fLo = FilterAdd2Sub2(fLo, q6Lo, p0Lo, p1Lo, p7Lo); + fHi = FilterAdd2Sub2(fHi, q6Hi, p0Hi, p1Hi, p7Hi); + op0 = Filter16Mask(flat2, op0, fLo, fHi); + Sse2.Store(s.ToPointer() - (1 * pitch), op0); + + fLo = FilterAdd2Sub2(fLo, q7Lo, q0Lo, p0Lo, p7Lo); + fHi = FilterAdd2Sub2(fHi, q7Hi, q0Hi, p0Hi, p7Hi); + oq0 = Filter16Mask(flat2, oq0, fLo, fHi); + Sse2.Store(s.ToPointer() - (0 * pitch), oq0); + + fLo = FilterAdd2Sub2(fLo, q7Lo, q1Lo, p6Lo, q0Lo); + fHi = FilterAdd2Sub2(fHi, q7Hi, q1Hi, p6Hi, q0Hi); + oq1 = Filter16Mask(flat2, oq1, fLo, fHi); + Sse2.Store(s.ToPointer() + (1 * pitch), oq1); + + fLo = FilterAdd2Sub2(fLo, q7Lo, q2Lo, p5Lo, q1Lo); + fHi = FilterAdd2Sub2(fHi, q7Hi, q2Hi, p5Hi, q1Hi); + oq2 = Filter16Mask(flat2, oq2, fLo, fHi); + Sse2.Store(s.ToPointer() + (2 * pitch), oq2); + + fLo = FilterAdd2Sub2(fLo, q7Lo, q3Lo, p4Lo, q2Lo); + fHi = FilterAdd2Sub2(fHi, q7Hi, q3Hi, p4Hi, q2Hi); + q3 = Filter16Mask(flat2, q3, fLo, fHi); + Sse2.Store(s.ToPointer() + (3 * pitch), q3); + + fLo = FilterAdd2Sub2(fLo, q7Lo, q4Lo, p3Lo, q3Lo); + fHi = FilterAdd2Sub2(fHi, q7Hi, q4Hi, p3Hi, q3Hi); + q4 = Filter16Mask(flat2, q4, fLo, fHi); + Sse2.Store(s.ToPointer() + (4 * pitch), q4); + + fLo = FilterAdd2Sub2(fLo, q7Lo, q5Lo, p2Lo, q4Lo); + fHi = FilterAdd2Sub2(fHi, q7Hi, q5Hi, p2Hi, q4Hi); + q5 = Filter16Mask(flat2, q5, fLo, fHi); + Sse2.Store(s.ToPointer() + (5 * pitch), q5); + + fLo = FilterAdd2Sub2(fLo, q7Lo, q6Lo, p1Lo, q5Lo); + fHi = FilterAdd2Sub2(fHi, q7Hi, q6Hi, p1Hi, q5Hi); + q6 = Filter16Mask(flat2, q6, fLo, fHi); + Sse2.Store(s.ToPointer() + (6 * pitch), q6); + } + // wide flat + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + } + } + + public static unsafe void LpfHorizontal8( + ArrayPtr s, + int pitch, + ReadOnlySpan blimit, + ReadOnlySpan limit, + ReadOnlySpan thresh) + { + Vector128 flatOp2; + Vector128 flatOp1; + Vector128 flatOp0; + Vector128 flatOq2; + Vector128 flatOq1; + Vector128 flatOq0; + Vector128 zero = Vector128.Zero; + Vector128 blimitV, limitV, threshV; + + fixed (byte* pBLimit = blimit, pLimit = limit, pThresh = thresh) + { + blimitV = Sse2.LoadVector128(pBLimit); + limitV = Sse2.LoadVector128(pLimit); + threshV = Sse2.LoadVector128(pThresh); + } + + Vector128 mask, hev, flat; + Vector128 p3, p2, p1, p0, q0, q1, q2, q3; + Vector128 q3P3, q2P2, q1P1, q0P0, p1Q1, p0Q0; + + q3P3 = Sse2.UnpackLow( + Sse2.LoadScalarVector128((long*)(s.ToPointer() - (4 * pitch))), + Sse2.LoadScalarVector128((long*)(s.ToPointer() + (3 * pitch)))).AsByte(); + q2P2 = Sse2.UnpackLow( + Sse2.LoadScalarVector128((long*)(s.ToPointer() - (3 * pitch))), + Sse2.LoadScalarVector128((long*)(s.ToPointer() + (2 * pitch)))).AsByte(); + q1P1 = Sse2.UnpackLow( + Sse2.LoadScalarVector128((long*)(s.ToPointer() - (2 * pitch))), + Sse2.LoadScalarVector128((long*)(s.ToPointer() + (1 * pitch)))).AsByte(); + q0P0 = Sse2.UnpackLow( + Sse2.LoadScalarVector128((long*)(s.ToPointer() - (1 * pitch))), + Sse2.LoadScalarVector128((long*)(s.ToPointer() - (0 * pitch)))).AsByte(); + p1Q1 = Sse2.Shuffle(q1P1.AsInt32(), 78).AsByte(); + p0Q0 = Sse2.Shuffle(q0P0.AsInt32(), 78).AsByte(); + + { + // filter_mask and hev_mask + Vector128 one = Vector128.Create((byte)1); + Vector128 fe = Vector128.Create((byte)0xfe); + Vector128 ff = Sse2.CompareEqual(fe, fe); + Vector128 absP1Q1, absP0Q0, absQ1Q0, absP1P0, work; + absP1P0 = AbsDiff(q1P1, q0P0); + absQ1Q0 = Sse2.ShiftRightLogical128BitLane(absP1P0, 8); + + absP0Q0 = AbsDiff(q0P0, p0Q0); + absP1Q1 = AbsDiff(q1P1, p1Q1); + flat = Sse2.Max(absP1P0, absQ1Q0); + hev = Sse2.SubtractSaturate(flat, threshV); + hev = Sse2.Xor(Sse2.CompareEqual(hev, zero), ff); + + absP0Q0 = Sse2.AddSaturate(absP0Q0, absP0Q0); + absP1Q1 = Sse2.ShiftRightLogical(Sse2.And(absP1Q1, fe).AsInt16(), 1).AsByte(); + mask = Sse2.SubtractSaturate(Sse2.AddSaturate(absP0Q0, absP1Q1), blimitV); + mask = Sse2.Xor(Sse2.CompareEqual(mask, zero), ff); + // mask |= (abs(p0 - q0) * 2 + abs(p1 - q1) / 2 > blimit) * -1; + mask = Sse2.Max(absP1P0, mask); + // mask |= (abs(p1 - p0) > limit) * -1; + // mask |= (abs(q1 - q0) > limit) * -1; + + work = Sse2.Max(AbsDiff(q2P2, q1P1), AbsDiff(q3P3, q2P2)); + mask = Sse2.Max(work, mask); + mask = Sse2.Max(mask, Sse2.ShiftRightLogical128BitLane(mask, 8)); + mask = Sse2.SubtractSaturate(mask, limitV); + mask = Sse2.CompareEqual(mask, zero); + + // flat_mask4 + + flat = Sse2.Max(AbsDiff(q2P2, q0P0), AbsDiff(q3P3, q0P0)); + flat = Sse2.Max(absP1P0, flat); + flat = Sse2.Max(flat, Sse2.ShiftRightLogical128BitLane(flat, 8)); + flat = Sse2.SubtractSaturate(flat, one); + flat = Sse2.CompareEqual(flat, zero); + flat = Sse2.And(flat, mask); + } + + { + Vector128 four = Vector128.Create((short)4); + { + Vector128 workpA, workpB, workpShft; + p3 = Sse2.UnpackLow( + Sse2.LoadScalarVector128((long*)(s.ToPointer() - (4 * pitch))).AsByte(), zero); + p2 = Sse2.UnpackLow( + Sse2.LoadScalarVector128((long*)(s.ToPointer() - (3 * pitch))).AsByte(), zero); + p1 = Sse2.UnpackLow( + Sse2.LoadScalarVector128((long*)(s.ToPointer() - (2 * pitch))).AsByte(), zero); + p0 = Sse2.UnpackLow( + Sse2.LoadScalarVector128((long*)(s.ToPointer() - (1 * pitch))).AsByte(), zero); + q0 = Sse2.UnpackLow( + Sse2.LoadScalarVector128((long*)(s.ToPointer() - (0 * pitch))).AsByte(), zero); + q1 = Sse2.UnpackLow( + Sse2.LoadScalarVector128((long*)(s.ToPointer() + (1 * pitch))).AsByte(), zero); + q2 = Sse2.UnpackLow( + Sse2.LoadScalarVector128((long*)(s.ToPointer() + (2 * pitch))).AsByte(), zero); + q3 = Sse2.UnpackLow( + Sse2.LoadScalarVector128((long*)(s.ToPointer() + (3 * pitch))).AsByte(), zero); + + workpA = Sse2.Add(Sse2.Add(p3.AsInt16(), p3.AsInt16()), Sse2.Add(p2.AsInt16(), p1.AsInt16())); + workpA = Sse2.Add(Sse2.Add(workpA, four), p0.AsInt16()); + workpB = Sse2.Add(Sse2.Add(q0.AsInt16(), p2.AsInt16()), p3.AsInt16()); + workpShft = Sse2.ShiftRightLogical(Sse2.Add(workpA, workpB), 3); + Sse2.StoreScalar((long*)&flatOp2, Sse2.PackUnsignedSaturate(workpShft, workpShft).AsInt64()); + + workpB = Sse2.Add(Sse2.Add(q0.AsInt16(), q1.AsInt16()), p1.AsInt16()); + workpShft = Sse2.ShiftRightLogical(Sse2.Add(workpA, workpB), 3); + Sse2.StoreScalar((long*)&flatOp1, Sse2.PackUnsignedSaturate(workpShft, workpShft).AsInt64()); + + workpA = Sse2.Add(Sse2.Subtract(workpA, p3.AsInt16()), q2.AsInt16()); + workpB = Sse2.Add(Sse2.Subtract(workpB, p1.AsInt16()), p0.AsInt16()); + workpShft = Sse2.ShiftRightLogical(Sse2.Add(workpA, workpB), 3); + Sse2.StoreScalar((long*)&flatOp0, Sse2.PackUnsignedSaturate(workpShft, workpShft).AsInt64()); + + workpA = Sse2.Add(Sse2.Subtract(workpA, p3.AsInt16()), q3.AsInt16()); + workpB = Sse2.Add(Sse2.Subtract(workpB, p0.AsInt16()), q0.AsInt16()); + workpShft = Sse2.ShiftRightLogical(Sse2.Add(workpA, workpB), 3); + Sse2.StoreScalar((long*)&flatOq0, Sse2.PackUnsignedSaturate(workpShft, workpShft).AsInt64()); + + workpA = Sse2.Add(Sse2.Subtract(workpA, p2.AsInt16()), q3.AsInt16()); + workpB = Sse2.Add(Sse2.Subtract(workpB, q0.AsInt16()), q1.AsInt16()); + workpShft = Sse2.ShiftRightLogical(Sse2.Add(workpA, workpB), 3); + Sse2.StoreScalar((long*)&flatOq1, Sse2.PackUnsignedSaturate(workpShft, workpShft).AsInt64()); + + workpA = Sse2.Add(Sse2.Subtract(workpA, p1.AsInt16()), q3.AsInt16()); + workpB = Sse2.Add(Sse2.Subtract(workpB, q1.AsInt16()), q2.AsInt16()); + workpShft = Sse2.ShiftRightLogical(Sse2.Add(workpA, workpB), 3); + Sse2.StoreScalar((long*)&flatOq2, Sse2.PackUnsignedSaturate(workpShft, workpShft).AsInt64()); + } + } + // lp filter + { + Vector128 t4 = Vector128.Create((byte)4); + Vector128 t3 = Vector128.Create((byte)3); + Vector128 t80 = Vector128.Create((byte)0x80); + Vector128 t1 = Vector128.Create((byte)0x1); + Vector128 ps1 = + Sse2.Xor(Sse2.LoadScalarVector128((long*)(s.ToPointer() - (2 * pitch))).AsByte(), + t80); + Vector128 ps0 = + Sse2.Xor(Sse2.LoadScalarVector128((long*)(s.ToPointer() - (1 * pitch))).AsByte(), + t80); + Vector128 qs0 = + Sse2.Xor(Sse2.LoadScalarVector128((long*)(s.ToPointer() + (0 * pitch))).AsByte(), + t80); + Vector128 qs1 = + Sse2.Xor(Sse2.LoadScalarVector128((long*)(s.ToPointer() + (1 * pitch))).AsByte(), + t80); + Vector128 filt; + Vector128 workA; + Vector128 filter1, filter2; + + filt = Sse2.And(Sse2.SubtractSaturate(ps1.AsSByte(), qs1.AsSByte()).AsByte(), hev); + workA = Sse2.SubtractSaturate(qs0.AsSByte(), ps0.AsSByte()).AsByte(); + filt = Sse2.AddSaturate(filt.AsSByte(), workA.AsSByte()).AsByte(); + filt = Sse2.AddSaturate(filt.AsSByte(), workA.AsSByte()).AsByte(); + filt = Sse2.AddSaturate(filt.AsSByte(), workA.AsSByte()).AsByte(); + // (vpx_filter + 3 * (qs0 - ps0)) & mask + filt = Sse2.And(filt, mask); + + filter1 = Sse2.AddSaturate(filt.AsSByte(), t4.AsSByte()).AsByte(); + filter2 = Sse2.AddSaturate(filt.AsSByte(), t3.AsSByte()).AsByte(); + + // Filter1 >> 3 + filter1 = Sse2.UnpackLow(zero, filter1); + filter1 = Sse2.ShiftRightArithmetic(filter1.AsInt16(), 11).AsByte(); + filter1 = Sse2.PackSignedSaturate(filter1.AsInt16(), filter1.AsInt16()).AsByte(); + + // Filter2 >> 3 + filter2 = Sse2.UnpackLow(zero, filter2); + filter2 = Sse2.ShiftRightArithmetic(filter2.AsInt16(), 11).AsByte(); + filter2 = Sse2.PackSignedSaturate(filter2.AsInt16(), zero.AsInt16()).AsByte(); + + // filt >> 1 + filt = Sse2.AddSaturate(filter1.AsSByte(), t1.AsSByte()).AsByte(); + filt = Sse2.UnpackLow(zero, filt); + filt = Sse2.ShiftRightArithmetic(filt.AsInt16(), 9).AsByte(); + filt = Sse2.PackSignedSaturate(filt.AsInt16(), zero.AsInt16()).AsByte(); + + filt = Sse2.AndNot(hev, filt); + + workA = Sse2.Xor(Sse2.SubtractSaturate(qs0.AsSByte(), filter1.AsSByte()).AsByte(), t80); + q0 = Sse2.LoadScalarVector128((long*)&flatOq0).AsByte(); + workA = Sse2.AndNot(flat, workA); + q0 = Sse2.And(flat, q0); + q0 = Sse2.Or(workA, q0); + + workA = Sse2.Xor(Sse2.SubtractSaturate(qs1.AsSByte(), filt.AsSByte()).AsByte(), t80); + q1 = Sse2.LoadScalarVector128((long*)&flatOq1).AsByte(); + workA = Sse2.AndNot(flat, workA); + q1 = Sse2.And(flat, q1); + q1 = Sse2.Or(workA, q1); + + workA = Sse2.LoadVector128(s.ToPointer() + (2 * pitch)); + q2 = Sse2.LoadScalarVector128((long*)&flatOq2).AsByte(); + workA = Sse2.AndNot(flat, workA); + q2 = Sse2.And(flat, q2); + q2 = Sse2.Or(workA, q2); + + workA = Sse2.Xor(Sse2.AddSaturate(ps0.AsSByte(), filter2.AsSByte()).AsByte(), t80); + p0 = Sse2.LoadScalarVector128((long*)&flatOp0).AsByte(); + workA = Sse2.AndNot(flat, workA); + p0 = Sse2.And(flat, p0); + p0 = Sse2.Or(workA, p0); + + workA = Sse2.Xor(Sse2.AddSaturate(ps1.AsSByte(), filt.AsSByte()).AsByte(), t80); + p1 = Sse2.LoadScalarVector128((long*)&flatOp1).AsByte(); + workA = Sse2.AndNot(flat, workA); + p1 = Sse2.And(flat, p1); + p1 = Sse2.Or(workA, p1); + + workA = Sse2.LoadVector128(s.ToPointer() - (3 * pitch)); + p2 = Sse2.LoadScalarVector128((long*)&flatOp2).AsByte(); + workA = Sse2.AndNot(flat, workA); + p2 = Sse2.And(flat, p2); + p2 = Sse2.Or(workA, p2); + + Sse2.StoreScalar((long*)(s.ToPointer() - (3 * pitch)), p2.AsInt64()); + Sse2.StoreScalar((long*)(s.ToPointer() - (2 * pitch)), p1.AsInt64()); + Sse2.StoreScalar((long*)(s.ToPointer() - (1 * pitch)), p0.AsInt64()); + Sse2.StoreScalar((long*)(s.ToPointer() + (0 * pitch)), q0.AsInt64()); + Sse2.StoreScalar((long*)(s.ToPointer() + (1 * pitch)), q1.AsInt64()); + Sse2.StoreScalar((long*)(s.ToPointer() + (2 * pitch)), q2.AsInt64()); + } + } + + public static unsafe void LpfHorizontal8Dual( + ArrayPtr s, + int pitch, + ReadOnlySpan blimit0, + ReadOnlySpan limit0, + ReadOnlySpan thresh0, + ReadOnlySpan blimit1, + ReadOnlySpan limit1, + ReadOnlySpan thresh1) + { + Vector128 flatOp2; + Vector128 flatOp1; + Vector128 flatOp0; + Vector128 flatOq2; + Vector128 flatOq1; + Vector128 flatOq0; + Vector128 zero = Vector128.Zero; + Vector128 blimit, limit, thresh; + + fixed (byte* pBLimit0 = blimit0, pLimit0 = limit0, pThresh0 = thresh0, + pBLimit1 = blimit1, pLimit1 = limit1, pThresh1 = thresh1) + { + blimit = Sse2.UnpackLow(Sse2.LoadVector128(pBLimit0).AsInt64(), Sse2.LoadVector128(pBLimit1).AsInt64()) + .AsByte(); + limit = Sse2.UnpackLow(Sse2.LoadVector128(pLimit0).AsInt64(), Sse2.LoadVector128(pLimit1).AsInt64()) + .AsByte(); + thresh = Sse2.UnpackLow(Sse2.LoadVector128(pThresh0).AsInt64(), Sse2.LoadVector128(pThresh1).AsInt64()) + .AsByte(); + } + + Vector128 mask, hev, flat; + Vector128 p3, p2, p1, p0, q0, q1, q2, q3; + + p3 = Sse2.LoadVector128(s.ToPointer() - (4 * pitch)); + p2 = Sse2.LoadVector128(s.ToPointer() - (3 * pitch)); + p1 = Sse2.LoadVector128(s.ToPointer() - (2 * pitch)); + p0 = Sse2.LoadVector128(s.ToPointer() - (1 * pitch)); + q0 = Sse2.LoadVector128(s.ToPointer() - (0 * pitch)); + q1 = Sse2.LoadVector128(s.ToPointer() + (1 * pitch)); + q2 = Sse2.LoadVector128(s.ToPointer() + (2 * pitch)); + q3 = Sse2.LoadVector128(s.ToPointer() + (3 * pitch)); + { + Vector128 absP1P0 = Sse2.Or(Sse2.SubtractSaturate(p1, p0), Sse2.SubtractSaturate(p0, p1)); + Vector128 absQ1Q0 = Sse2.Or(Sse2.SubtractSaturate(q1, q0), Sse2.SubtractSaturate(q0, q1)); + Vector128 one = Vector128.Create((byte)1); + Vector128 fe = Vector128.Create((byte)0xfe); + Vector128 ff = Sse2.CompareEqual(absP1P0, absP1P0); + Vector128 absP0Q0 = Sse2.Or(Sse2.SubtractSaturate(p0, q0), Sse2.SubtractSaturate(q0, p0)); + Vector128 absP1Q1 = Sse2.Or(Sse2.SubtractSaturate(p1, q1), Sse2.SubtractSaturate(q1, p1)); + Vector128 work; + + // filter_mask and hev_mask + flat = Sse2.Max(absP1P0, absQ1Q0); + hev = Sse2.SubtractSaturate(flat, thresh); + hev = Sse2.Xor(Sse2.CompareEqual(hev, zero), ff); + + absP0Q0 = Sse2.AddSaturate(absP0Q0, absP0Q0); + absP1Q1 = Sse2.ShiftRightLogical(Sse2.And(absP1Q1, fe).AsInt16(), 1).AsByte(); + mask = Sse2.SubtractSaturate(Sse2.AddSaturate(absP0Q0, absP1Q1), blimit); + mask = Sse2.Xor(Sse2.CompareEqual(mask, zero), ff); + // mask |= (abs(p0 - q0) * 2 + abs(p1 - q1) / 2 > blimit) * -1; + mask = Sse2.Max(flat, mask); + // mask |= (abs(p1 - p0) > limit) * -1; + // mask |= (abs(q1 - q0) > limit) * -1; + work = Sse2.Max( + Sse2.Or(Sse2.SubtractSaturate(p2, p1), Sse2.SubtractSaturate(p1, p2)), + Sse2.Or(Sse2.SubtractSaturate(p3, p2), Sse2.SubtractSaturate(p2, p3))); + mask = Sse2.Max(work, mask); + work = Sse2.Max( + Sse2.Or(Sse2.SubtractSaturate(q2, q1), Sse2.SubtractSaturate(q1, q2)), + Sse2.Or(Sse2.SubtractSaturate(q3, q2), Sse2.SubtractSaturate(q2, q3))); + mask = Sse2.Max(work, mask); + mask = Sse2.SubtractSaturate(mask, limit); + mask = Sse2.CompareEqual(mask, zero); + + // flat_mask4 + work = Sse2.Max( + Sse2.Or(Sse2.SubtractSaturate(p2, p0), Sse2.SubtractSaturate(p0, p2)), + Sse2.Or(Sse2.SubtractSaturate(q2, q0), Sse2.SubtractSaturate(q0, q2))); + flat = Sse2.Max(work, flat); + work = Sse2.Max( + Sse2.Or(Sse2.SubtractSaturate(p3, p0), Sse2.SubtractSaturate(p0, p3)), + Sse2.Or(Sse2.SubtractSaturate(q3, q0), Sse2.SubtractSaturate(q0, q3))); + flat = Sse2.Max(work, flat); + flat = Sse2.SubtractSaturate(flat, one); + flat = Sse2.CompareEqual(flat, zero); + flat = Sse2.And(flat, mask); + } + { + Vector128 four = Vector128.Create((short)4); + ArrayPtr src = s; + int i = 0; + + do + { + Vector128 workpA, workpB, workpShft; + p3 = Sse2.UnpackLow( + Sse2.LoadScalarVector128((long*)(src.ToPointer() - (4 * pitch))).AsByte(), zero); + p2 = Sse2.UnpackLow( + Sse2.LoadScalarVector128((long*)(src.ToPointer() - (3 * pitch))).AsByte(), zero); + p1 = Sse2.UnpackLow( + Sse2.LoadScalarVector128((long*)(src.ToPointer() - (2 * pitch))).AsByte(), zero); + p0 = Sse2.UnpackLow( + Sse2.LoadScalarVector128((long*)(src.ToPointer() - (1 * pitch))).AsByte(), zero); + q0 = Sse2.UnpackLow( + Sse2.LoadScalarVector128((long*)(src.ToPointer() - (0 * pitch))).AsByte(), zero); + q1 = Sse2.UnpackLow( + Sse2.LoadScalarVector128((long*)(src.ToPointer() + (1 * pitch))).AsByte(), zero); + q2 = Sse2.UnpackLow( + Sse2.LoadScalarVector128((long*)(src.ToPointer() + (2 * pitch))).AsByte(), zero); + q3 = Sse2.UnpackLow( + Sse2.LoadScalarVector128((long*)(src.ToPointer() + (3 * pitch))).AsByte(), zero); + + workpA = Sse2.Add(Sse2.Add(p3.AsInt16(), p3.AsInt16()), Sse2.Add(p2.AsInt16(), p1.AsInt16())); + workpA = Sse2.Add(Sse2.Add(workpA, four), p0.AsInt16()); + workpB = Sse2.Add(Sse2.Add(q0.AsInt16(), p2.AsInt16()), p3.AsInt16()); + workpShft = Sse2.ShiftRightLogical(Sse2.Add(workpA, workpB), 3); + Sse2.StoreScalar((long*)&flatOp2 + i, Sse2.PackUnsignedSaturate(workpShft, workpShft).AsInt64()); + + workpB = Sse2.Add(Sse2.Add(q0.AsInt16(), q1.AsInt16()), p1.AsInt16()); + workpShft = Sse2.ShiftRightLogical(Sse2.Add(workpA, workpB), 3); + Sse2.StoreScalar((long*)&flatOp1 + i, Sse2.PackUnsignedSaturate(workpShft, workpShft).AsInt64()); + + workpA = Sse2.Add(Sse2.Subtract(workpA, p3.AsInt16()), q2.AsInt16()); + workpB = Sse2.Add(Sse2.Subtract(workpB, p1.AsInt16()), p0.AsInt16()); + workpShft = Sse2.ShiftRightLogical(Sse2.Add(workpA, workpB), 3); + Sse2.StoreScalar((long*)&flatOp0 + i, Sse2.PackUnsignedSaturate(workpShft, workpShft).AsInt64()); + + workpA = Sse2.Add(Sse2.Subtract(workpA, p3.AsInt16()), q3.AsInt16()); + workpB = Sse2.Add(Sse2.Subtract(workpB, p0.AsInt16()), q0.AsInt16()); + workpShft = Sse2.ShiftRightLogical(Sse2.Add(workpA, workpB), 3); + Sse2.StoreScalar((long*)&flatOq0 + i, Sse2.PackUnsignedSaturate(workpShft, workpShft).AsInt64()); + + workpA = Sse2.Add(Sse2.Subtract(workpA, p2.AsInt16()), q3.AsInt16()); + workpB = Sse2.Add(Sse2.Subtract(workpB, q0.AsInt16()), q1.AsInt16()); + workpShft = Sse2.ShiftRightLogical(Sse2.Add(workpA, workpB), 3); + Sse2.StoreScalar((long*)&flatOq1 + i, Sse2.PackUnsignedSaturate(workpShft, workpShft).AsInt64()); + + workpA = Sse2.Add(Sse2.Subtract(workpA, p1.AsInt16()), q3.AsInt16()); + workpB = Sse2.Add(Sse2.Subtract(workpB, q1.AsInt16()), q2.AsInt16()); + workpShft = Sse2.ShiftRightLogical(Sse2.Add(workpA, workpB), 3); + Sse2.StoreScalar((long*)&flatOq2 + i, Sse2.PackUnsignedSaturate(workpShft, workpShft).AsInt64()); + + src = src.Slice(8); + } while (++i < 2); + } + // lp filter + { + Vector128 t4 = Vector128.Create((byte)4); + Vector128 t3 = Vector128.Create((byte)3); + Vector128 t80 = Vector128.Create((byte)0x80); + Vector128 te0 = Vector128.Create((byte)0xe0); + Vector128 t1F = Vector128.Create((byte)0x1f); + Vector128 t1 = Vector128.Create((byte)0x1); + Vector128 t7F = Vector128.Create((byte)0x7f); + + Vector128 ps1 = Sse2.Xor(Sse2.LoadVector128(s.ToPointer() - (2 * pitch)), t80); + Vector128 ps0 = Sse2.Xor(Sse2.LoadVector128(s.ToPointer() - (1 * pitch)), t80); + Vector128 qs0 = Sse2.Xor(Sse2.LoadVector128(s.ToPointer() + (0 * pitch)), t80); + Vector128 qs1 = Sse2.Xor(Sse2.LoadVector128(s.ToPointer() + (1 * pitch)), t80); + Vector128 filt; + Vector128 workA; + Vector128 filter1, filter2; + + filt = Sse2.And(Sse2.SubtractSaturate(ps1.AsSByte(), qs1.AsSByte()).AsByte(), hev); + workA = Sse2.SubtractSaturate(qs0.AsSByte(), ps0.AsSByte()).AsByte(); + filt = Sse2.AddSaturate(filt.AsSByte(), workA.AsSByte()).AsByte(); + filt = Sse2.AddSaturate(filt.AsSByte(), workA.AsSByte()).AsByte(); + filt = Sse2.AddSaturate(filt.AsSByte(), workA.AsSByte()).AsByte(); + // (vpx_filter + 3 * (qs0 - ps0)) & mask + filt = Sse2.And(filt, mask); + + filter1 = Sse2.AddSaturate(filt.AsSByte(), t4.AsSByte()).AsByte(); + filter2 = Sse2.AddSaturate(filt.AsSByte(), t3.AsSByte()).AsByte(); + + // Filter1 >> 3 + workA = Sse2.CompareGreaterThan(zero.AsSByte(), filter1.AsSByte()).AsByte(); + filter1 = Sse2.ShiftRightLogical(filter1.AsInt16(), 3).AsByte(); + workA = Sse2.And(workA, te0); + filter1 = Sse2.And(filter1, t1F); + filter1 = Sse2.Or(filter1, workA); + + // Filter2 >> 3 + workA = Sse2.CompareGreaterThan(zero.AsSByte(), filter2.AsSByte()).AsByte(); + filter2 = Sse2.ShiftRightLogical(filter2.AsInt16(), 3).AsByte(); + workA = Sse2.And(workA, te0); + filter2 = Sse2.And(filter2, t1F); + filter2 = Sse2.Or(filter2, workA); + + // filt >> 1 + filt = Sse2.AddSaturate(filter1.AsSByte(), t1.AsSByte()).AsByte(); + workA = Sse2.CompareGreaterThan(zero.AsSByte(), filt.AsSByte()).AsByte(); + filt = Sse2.ShiftRightLogical(filt.AsInt16(), 1).AsByte(); + workA = Sse2.And(workA, t80); + filt = Sse2.And(filt, t7F); + filt = Sse2.Or(filt, workA); + + filt = Sse2.AndNot(hev, filt); + + workA = Sse2.Xor(Sse2.SubtractSaturate(qs0.AsSByte(), filter1.AsSByte()).AsByte(), t80); + q0 = Sse2.LoadVector128((byte*)&flatOq0); + workA = Sse2.AndNot(flat, workA); + q0 = Sse2.And(flat, q0); + q0 = Sse2.Or(workA, q0); + + workA = Sse2.Xor(Sse2.SubtractSaturate(qs1.AsSByte(), filt.AsSByte()).AsByte(), t80); + q1 = Sse2.LoadVector128((byte*)&flatOq1); + workA = Sse2.AndNot(flat, workA); + q1 = Sse2.And(flat, q1); + q1 = Sse2.Or(workA, q1); + + workA = Sse2.LoadVector128(s.ToPointer() + (2 * pitch)); + q2 = Sse2.LoadVector128((byte*)&flatOq2); + workA = Sse2.AndNot(flat, workA); + q2 = Sse2.And(flat, q2); + q2 = Sse2.Or(workA, q2); + + workA = Sse2.Xor(Sse2.AddSaturate(ps0.AsSByte(), filter2.AsSByte()).AsByte(), t80); + p0 = Sse2.LoadVector128((byte*)&flatOp0); + workA = Sse2.AndNot(flat, workA); + p0 = Sse2.And(flat, p0); + p0 = Sse2.Or(workA, p0); + + workA = Sse2.Xor(Sse2.AddSaturate(ps1.AsSByte(), filt.AsSByte()).AsByte(), t80); + p1 = Sse2.LoadVector128((byte*)&flatOp1); + workA = Sse2.AndNot(flat, workA); + p1 = Sse2.And(flat, p1); + p1 = Sse2.Or(workA, p1); + + workA = Sse2.LoadVector128(s.ToPointer() - (3 * pitch)); + p2 = Sse2.LoadVector128((byte*)&flatOp2); + workA = Sse2.AndNot(flat, workA); + p2 = Sse2.And(flat, p2); + p2 = Sse2.Or(workA, p2); + + Sse2.Store(s.ToPointer() - (3 * pitch), p2); + Sse2.Store(s.ToPointer() - (2 * pitch), p1); + Sse2.Store(s.ToPointer() - (1 * pitch), p0); + Sse2.Store(s.ToPointer() + (0 * pitch), q0); + Sse2.Store(s.ToPointer() + (1 * pitch), q1); + Sse2.Store(s.ToPointer() + (2 * pitch), q2); + } + } + + public static unsafe void LpfHorizontal4Dual( + ArrayPtr s, + int pitch, + ReadOnlySpan blimit0, + ReadOnlySpan limit0, + ReadOnlySpan thresh0, + ReadOnlySpan blimit1, + ReadOnlySpan limit1, + ReadOnlySpan thresh1) + { + Vector128 blimit, limit, thresh; + + fixed (byte* pBLimit0 = blimit0, pLimit0 = limit0, pThresh0 = thresh0, + pBLimit1 = blimit1, pLimit1 = limit1, pThresh1 = thresh1) + { + blimit = Sse2.UnpackLow(Sse2.LoadVector128(pBLimit0).AsInt64(), Sse2.LoadVector128(pBLimit1).AsInt64()) + .AsByte(); + limit = Sse2.UnpackLow(Sse2.LoadVector128(pLimit0).AsInt64(), Sse2.LoadVector128(pLimit1).AsInt64()) + .AsByte(); + thresh = Sse2.UnpackLow(Sse2.LoadVector128(pThresh0).AsInt64(), Sse2.LoadVector128(pThresh1).AsInt64()) + .AsByte(); + } + + Vector128 zero = Vector128.Zero; + Vector128 p3, p2, p1, p0, q0, q1, q2, q3; + Vector128 mask, hev, flat; + + p3 = Sse2.LoadVector128(s.ToPointer() - (4 * pitch)); + p2 = Sse2.LoadVector128(s.ToPointer() - (3 * pitch)); + p1 = Sse2.LoadVector128(s.ToPointer() - (2 * pitch)); + p0 = Sse2.LoadVector128(s.ToPointer() - (1 * pitch)); + q0 = Sse2.LoadVector128(s.ToPointer() - (0 * pitch)); + q1 = Sse2.LoadVector128(s.ToPointer() + (1 * pitch)); + q2 = Sse2.LoadVector128(s.ToPointer() + (2 * pitch)); + q3 = Sse2.LoadVector128(s.ToPointer() + (3 * pitch)); + + // filter_mask and hev_mask + { + Vector128 absP1P0 = Sse2.Or(Sse2.SubtractSaturate(p1, p0), Sse2.SubtractSaturate(p0, p1)); + Vector128 absQ1Q0 = Sse2.Or(Sse2.SubtractSaturate(q1, q0), Sse2.SubtractSaturate(q0, q1)); + Vector128 fe = Vector128.Create((byte)0xfe); + Vector128 ff = Sse2.CompareEqual(absP1P0, absP1P0); + Vector128 absP0Q0 = Sse2.Or(Sse2.SubtractSaturate(p0, q0), Sse2.SubtractSaturate(q0, p0)); + Vector128 absP1Q1 = Sse2.Or(Sse2.SubtractSaturate(p1, q1), Sse2.SubtractSaturate(q1, p1)); + Vector128 work; + + flat = Sse2.Max(absP1P0, absQ1Q0); + hev = Sse2.SubtractSaturate(flat, thresh); + hev = Sse2.Xor(Sse2.CompareEqual(hev, zero), ff); + + absP0Q0 = Sse2.AddSaturate(absP0Q0, absP0Q0); + absP1Q1 = Sse2.ShiftRightLogical(Sse2.And(absP1Q1, fe).AsInt16(), 1).AsByte(); + mask = Sse2.SubtractSaturate(Sse2.AddSaturate(absP0Q0, absP1Q1), blimit); + mask = Sse2.Xor(Sse2.CompareEqual(mask, zero), ff); + // mask |= (abs(p0 - q0) * 2 + abs(p1 - q1) / 2 > blimit) * -1; + mask = Sse2.Max(flat, mask); + // mask |= (abs(p1 - p0) > limit) * -1; + // mask |= (abs(q1 - q0) > limit) * -1; + work = Sse2.Max( + Sse2.Or(Sse2.SubtractSaturate(p2, p1), Sse2.SubtractSaturate(p1, p2)), + Sse2.Or(Sse2.SubtractSaturate(p3, p2), Sse2.SubtractSaturate(p2, p3))); + mask = Sse2.Max(work, mask); + work = Sse2.Max( + Sse2.Or(Sse2.SubtractSaturate(q2, q1), Sse2.SubtractSaturate(q1, q2)), + Sse2.Or(Sse2.SubtractSaturate(q3, q2), Sse2.SubtractSaturate(q2, q3))); + mask = Sse2.Max(work, mask); + mask = Sse2.SubtractSaturate(mask, limit); + mask = Sse2.CompareEqual(mask, zero); + } + + // filter4 + { + Vector128 t4 = Vector128.Create((byte)4); + Vector128 t3 = Vector128.Create((byte)3); + Vector128 t80 = Vector128.Create((byte)0x80); + Vector128 te0 = Vector128.Create((byte)0xe0); + Vector128 t1F = Vector128.Create((byte)0x1f); + Vector128 t1 = Vector128.Create((byte)0x1); + Vector128 t7F = Vector128.Create((byte)0x7f); + + Vector128 ps1 = Sse2.Xor(Sse2.LoadVector128(s.ToPointer() - (2 * pitch)), t80); + Vector128 ps0 = Sse2.Xor(Sse2.LoadVector128(s.ToPointer() - (1 * pitch)), t80); + Vector128 qs0 = Sse2.Xor(Sse2.LoadVector128(s.ToPointer() + (0 * pitch)), t80); + Vector128 qs1 = Sse2.Xor(Sse2.LoadVector128(s.ToPointer() + (1 * pitch)), t80); + Vector128 filt; + Vector128 workA; + Vector128 filter1, filter2; + + filt = Sse2.And(Sse2.SubtractSaturate(ps1.AsSByte(), qs1.AsSByte()).AsByte(), hev); + workA = Sse2.SubtractSaturate(qs0.AsSByte(), ps0.AsSByte()).AsByte(); + filt = Sse2.AddSaturate(filt.AsSByte(), workA.AsSByte()).AsByte(); + filt = Sse2.AddSaturate(filt.AsSByte(), workA.AsSByte()).AsByte(); + filt = Sse2.AddSaturate(filt.AsSByte(), workA.AsSByte()).AsByte(); + // (vpx_filter + 3 * (qs0 - ps0)) & mask + filt = Sse2.And(filt, mask); + + filter1 = Sse2.AddSaturate(filt.AsSByte(), t4.AsSByte()).AsByte(); + filter2 = Sse2.AddSaturate(filt.AsSByte(), t3.AsSByte()).AsByte(); + + // Filter1 >> 3 + workA = Sse2.CompareGreaterThan(zero.AsSByte(), filter1.AsSByte()).AsByte(); + filter1 = Sse2.ShiftRightLogical(filter1.AsInt16(), 3).AsByte(); + workA = Sse2.And(workA, te0); + filter1 = Sse2.And(filter1, t1F); + filter1 = Sse2.Or(filter1, workA); + + // Filter2 >> 3 + workA = Sse2.CompareGreaterThan(zero.AsSByte(), filter2.AsSByte()).AsByte(); + filter2 = Sse2.ShiftRightLogical(filter2.AsInt16(), 3).AsByte(); + workA = Sse2.And(workA, te0); + filter2 = Sse2.And(filter2, t1F); + filter2 = Sse2.Or(filter2, workA); + + // filt >> 1 + filt = Sse2.AddSaturate(filter1.AsSByte(), t1.AsSByte()).AsByte(); + workA = Sse2.CompareGreaterThan(zero.AsSByte(), filt.AsSByte()).AsByte(); + filt = Sse2.ShiftRightLogical(filt.AsInt16(), 1).AsByte(); + workA = Sse2.And(workA, t80); + filt = Sse2.And(filt, t7F); + filt = Sse2.Or(filt, workA); + + filt = Sse2.AndNot(hev, filt); + + q0 = Sse2.Xor(Sse2.SubtractSaturate(qs0.AsSByte(), filter1.AsSByte()).AsByte(), t80); + q1 = Sse2.Xor(Sse2.SubtractSaturate(qs1.AsSByte(), filt.AsSByte()).AsByte(), t80); + p0 = Sse2.Xor(Sse2.AddSaturate(ps0.AsSByte(), filter2.AsSByte()).AsByte(), t80); + p1 = Sse2.Xor(Sse2.AddSaturate(ps1.AsSByte(), filt.AsSByte()).AsByte(), t80); + + Sse2.Store(s.ToPointer() - (2 * pitch), p1); + Sse2.Store(s.ToPointer() - (1 * pitch), p0); + Sse2.Store(s.ToPointer() + (0 * pitch), q0); + Sse2.Store(s.ToPointer() + (1 * pitch), q1); + } + } + + private static unsafe void Transpose8X16( + ArrayPtr in0, + ArrayPtr in1, + int inP, + ArrayPtr output, + int outP) + { + Vector128 x0, x1, x2, x3, x4, x5, x6, x7; + Vector128 x8, x9, x10, x11, x12, x13, x14, x15; + + // 2-way interleave w/hoisting of unpacks + x0 = Sse2.LoadScalarVector128((long*)in0.ToPointer()).AsByte(); // 1 + x1 = Sse2.LoadScalarVector128((long*)(in0.ToPointer() + inP)).AsByte(); // 3 + x0 = Sse2.UnpackLow(x0, x1); // 1 + + x2 = Sse2.LoadScalarVector128((long*)(in0.ToPointer() + (2 * inP))).AsByte(); // 5 + x3 = Sse2.LoadScalarVector128((long*)(in0.ToPointer() + (3 * inP))).AsByte(); // 7 + x1 = Sse2.UnpackLow(x2, x3); // 2 + + x4 = Sse2.LoadScalarVector128((long*)(in0.ToPointer() + (4 * inP))).AsByte(); // 9 + x5 = Sse2.LoadScalarVector128((long*)(in0.ToPointer() + (5 * inP))).AsByte(); // 11 + x2 = Sse2.UnpackLow(x4, x5); // 3 + + x6 = Sse2.LoadScalarVector128((long*)(in0.ToPointer() + (6 * inP))).AsByte(); // 13 + x7 = Sse2.LoadScalarVector128((long*)(in0.ToPointer() + (7 * inP))).AsByte(); // 15 + x3 = Sse2.UnpackLow(x6, x7); // 4 + x4 = Sse2.UnpackLow(x0.AsInt16(), x1.AsInt16()).AsByte(); // 9 + + x8 = Sse2.LoadScalarVector128((long*)in1.ToPointer()).AsByte(); // 2 + x9 = Sse2.LoadScalarVector128((long*)(in1.ToPointer() + inP)).AsByte(); // 4 + x8 = Sse2.UnpackLow(x8, x9); // 5 + x5 = Sse2.UnpackLow(x2.AsInt16(), x3.AsInt16()).AsByte(); // 10 + + x10 = Sse2.LoadScalarVector128((long*)(in1.ToPointer() + (2 * inP))).AsByte(); // 6 + x11 = Sse2.LoadScalarVector128((long*)(in1.ToPointer() + (3 * inP))).AsByte(); // 8 + x9 = Sse2.UnpackLow(x10, x11); // 6 + + x12 = Sse2.LoadScalarVector128((long*)(in1.ToPointer() + (4 * inP))).AsByte(); // 10 + x13 = Sse2.LoadScalarVector128((long*)(in1.ToPointer() + (5 * inP))).AsByte(); // 12 + x10 = Sse2.UnpackLow(x12, x13); // 7 + x12 = Sse2.UnpackLow(x8.AsInt16(), x9.AsInt16()).AsByte(); // 11 + + x14 = Sse2.LoadScalarVector128((long*)(in1.ToPointer() + (6 * inP))).AsByte(); // 14 + x15 = Sse2.LoadScalarVector128((long*)(in1.ToPointer() + (7 * inP))).AsByte(); // 16 + x11 = Sse2.UnpackLow(x14, x15); // 8 + x13 = Sse2.UnpackLow(x10.AsInt16(), x11.AsInt16()).AsByte(); // 12 + + x6 = Sse2.UnpackLow(x4.AsInt32(), x5.AsInt32()).AsByte(); // 13 + x7 = Sse2.UnpackHigh(x4.AsInt32(), x5.AsInt32()).AsByte(); // 14 + x14 = Sse2.UnpackLow(x12.AsInt32(), x13.AsInt32()).AsByte(); // 15 + x15 = Sse2.UnpackHigh(x12.AsInt32(), x13.AsInt32()).AsByte(); // 16 + + // Store first 4-line result + Sse2.Store(output.ToPointer(), Sse2.UnpackLow(x6.AsInt64(), x14.AsInt64()).AsByte()); + Sse2.Store(output.ToPointer() + outP, Sse2.UnpackHigh(x6.AsInt64(), x14.AsInt64()).AsByte()); + Sse2.Store(output.ToPointer() + (2 * outP), Sse2.UnpackLow(x7.AsInt64(), x15.AsInt64()).AsByte()); + Sse2.Store(output.ToPointer() + (3 * outP), Sse2.UnpackHigh(x7.AsInt64(), x15.AsInt64()).AsByte()); + + x4 = Sse2.UnpackHigh(x0.AsInt16(), x1.AsInt16()).AsByte(); + x5 = Sse2.UnpackHigh(x2.AsInt16(), x3.AsInt16()).AsByte(); + x12 = Sse2.UnpackHigh(x8.AsInt16(), x9.AsInt16()).AsByte(); + x13 = Sse2.UnpackHigh(x10.AsInt16(), x11.AsInt16()).AsByte(); + + x6 = Sse2.UnpackLow(x4.AsInt32(), x5.AsInt32()).AsByte(); + x7 = Sse2.UnpackHigh(x4.AsInt32(), x5.AsInt32()).AsByte(); + x14 = Sse2.UnpackLow(x12.AsInt32(), x13.AsInt32()).AsByte(); + x15 = Sse2.UnpackHigh(x12.AsInt32(), x13.AsInt32()).AsByte(); + + // Store second 4-line result + Sse2.Store(output.ToPointer() + (4 * outP), Sse2.UnpackLow(x6.AsInt64(), x14.AsInt64()).AsByte()); + Sse2.Store(output.ToPointer() + (5 * outP), Sse2.UnpackHigh(x6.AsInt64(), x14.AsInt64()).AsByte()); + Sse2.Store(output.ToPointer() + (6 * outP), Sse2.UnpackLow(x7.AsInt64(), x15.AsInt64()).AsByte()); + Sse2.Store(output.ToPointer() + (7 * outP), Sse2.UnpackHigh(x7.AsInt64(), x15.AsInt64()).AsByte()); + } + + private static unsafe void Transpose( + ReadOnlySpan> src, + int inP, + ReadOnlySpan> dst, + int outP, + int num8X8ToTranspose) + { + int idx8X8 = 0; + Vector128 x0, x1, x2, x3, x4, x5, x6, x7; + + do + { + ArrayPtr input = src[idx8X8]; + ArrayPtr output = dst[idx8X8]; + + x0 = Sse2.LoadScalarVector128((long*)(input.ToPointer() + (0 * inP))) + .AsByte(); // 00 01 02 03 04 05 06 07 + x1 = Sse2.LoadScalarVector128((long*)(input.ToPointer() + (1 * inP))) + .AsByte(); // 10 11 12 13 14 15 16 17 + // 00 10 01 11 02 12 03 13 04 14 05 15 06 16 07 17 + x0 = Sse2.UnpackLow(x0, x1); + + x2 = Sse2.LoadScalarVector128((long*)(input.ToPointer() + (2 * inP))) + .AsByte(); // 20 21 22 23 24 25 26 27 + x3 = Sse2.LoadScalarVector128((long*)(input.ToPointer() + (3 * inP))) + .AsByte(); // 30 31 32 33 34 35 36 37 + // 20 30 21 31 22 32 23 33 24 34 25 35 26 36 27 37 + x1 = Sse2.UnpackLow(x2, x3); + + x4 = Sse2.LoadScalarVector128((long*)(input.ToPointer() + (4 * inP))) + .AsByte(); // 40 41 42 43 44 45 46 47 + x5 = Sse2.LoadScalarVector128((long*)(input.ToPointer() + (5 * inP))) + .AsByte(); // 50 51 52 53 54 55 56 57 + // 40 50 41 51 42 52 43 53 44 54 45 55 46 56 47 57 + x2 = Sse2.UnpackLow(x4, x5); + + x6 = Sse2.LoadScalarVector128((long*)(input.ToPointer() + (6 * inP))) + .AsByte(); // 60 61 62 63 64 65 66 67 + x7 = Sse2.LoadScalarVector128((long*)(input.ToPointer() + (7 * inP))) + .AsByte(); // 70 71 72 73 74 75 76 77 + // 60 70 61 71 62 72 63 73 64 74 65 75 66 76 67 77 + x3 = Sse2.UnpackLow(x6, x7); + + // 00 10 20 30 01 11 21 31 02 12 22 32 03 13 23 33 + x4 = Sse2.UnpackLow(x0.AsInt16(), x1.AsInt16()).AsByte(); + // 40 50 60 70 41 51 61 71 42 52 62 72 43 53 63 73 + x5 = Sse2.UnpackLow(x2.AsInt16(), x3.AsInt16()).AsByte(); + // 00 10 20 30 40 50 60 70 01 11 21 31 41 51 61 71 + x6 = Sse2.UnpackLow(x4.AsInt32(), x5.AsInt32()).AsByte(); + Sse2.StoreScalar((long*)(output.ToPointer() + (0 * outP)), x6.AsInt64()); // 00 10 20 30 40 50 60 70 + Sse2.StoreHigh((double*)(output.ToPointer() + (1 * outP)), x6.AsDouble()); // 01 11 21 31 41 51 61 71 + // 02 12 22 32 42 52 62 72 03 13 23 33 43 53 63 73 + x7 = Sse2.UnpackHigh(x4.AsInt32(), x5.AsInt32()).AsByte(); + Sse2.StoreScalar((long*)(output.ToPointer() + (2 * outP)), x7.AsInt64()); // 02 12 22 32 42 52 62 72 + Sse2.StoreHigh((double*)(output.ToPointer() + (3 * outP)), x7.AsDouble()); // 03 13 23 33 43 53 63 73 + + // 04 14 24 34 05 15 25 35 06 16 26 36 07 17 27 37 + x4 = Sse2.UnpackHigh(x0.AsInt16(), x1.AsInt16()).AsByte(); + // 44 54 64 74 45 55 65 75 46 56 66 76 47 57 67 77 + x5 = Sse2.UnpackHigh(x2.AsInt16(), x3.AsInt16()).AsByte(); + // 04 14 24 34 44 54 64 74 05 15 25 35 45 55 65 75 + x6 = Sse2.UnpackLow(x4.AsInt32(), x5.AsInt32()).AsByte(); + Sse2.StoreScalar((long*)(output.ToPointer() + (4 * outP)), x6.AsInt64()); // 04 14 24 34 44 54 64 74 + Sse2.StoreHigh((double*)(output.ToPointer() + (5 * outP)), x6.AsDouble()); // 05 15 25 35 45 55 65 75 + // 06 16 26 36 46 56 66 76 07 17 27 37 47 57 67 77 + x7 = Sse2.UnpackHigh(x4.AsInt32(), x5.AsInt32()).AsByte(); + + Sse2.StoreScalar((long*)(output.ToPointer() + (6 * outP)), x7.AsInt64()); // 06 16 26 36 46 56 66 76 + Sse2.StoreHigh((double*)(output.ToPointer() + (7 * outP)), x7.AsDouble()); // 07 17 27 37 47 57 67 77 + } while (++idx8X8 < num8X8ToTranspose); + } + + public static unsafe void LpfVertical4Dual( + ArrayPtr s, + int pitch, + ReadOnlySpan blimit0, + ReadOnlySpan limit0, + ReadOnlySpan thresh0, + ReadOnlySpan blimit1, + ReadOnlySpan limit1, + ReadOnlySpan thresh1) + { + ulong* tDstStorage = stackalloc ulong[16]; + ArrayPtr tDst = new((byte*)tDstStorage, 16 * 8); + Span> src = stackalloc ArrayPtr[2]; + Span> dst = stackalloc ArrayPtr[2]; + + // Transpose 8x16 + Transpose8X16(s.Slice(-4), s.Slice(-4 + (pitch * 8)), pitch, tDst, 16); + + // Loop filtering + LpfHorizontal4Dual(tDst.Slice(4 * 16), 16, blimit0, limit0, thresh0, blimit1, limit1, thresh1); + src[0] = tDst; + src[1] = tDst.Slice(8); + dst[0] = s.Slice(-4); + dst[1] = s.Slice(-4 + (pitch * 8)); + + // Transpose back + Transpose(src, 16, dst, pitch, 2); + } + + public static unsafe void LpfVertical8( + ArrayPtr s, + int pitch, + ReadOnlySpan blimit, + ReadOnlySpan limit, + ReadOnlySpan thresh) + { + ulong* tDstStorage = stackalloc ulong[8]; + ArrayPtr tDst = new((byte*)tDstStorage, 8 * 8); + Span> src = stackalloc ArrayPtr[1]; + Span> dst = stackalloc ArrayPtr[1]; + + // Transpose 8x8 + src[0] = s.Slice(-4); + dst[0] = tDst; + + Transpose(src, pitch, dst, 8, 1); + + // Loop filtering + LpfHorizontal8(tDst.Slice(4 * 8), 8, blimit, limit, thresh); + + // Transpose back + Transpose(dst, 8, src, pitch, 1); + } + + public static unsafe void LpfVertical8Dual( + ArrayPtr s, + int pitch, + ReadOnlySpan blimit0, + ReadOnlySpan limit0, + ReadOnlySpan thresh0, + ReadOnlySpan blimit1, + ReadOnlySpan limit1, + ReadOnlySpan thresh1) + { + ulong* tDstStorage = stackalloc ulong[16]; + ArrayPtr tDst = new((byte*)tDstStorage, 16 * 8); + Span> src = stackalloc ArrayPtr[2]; + Span> dst = stackalloc ArrayPtr[2]; + + // Transpose 8x16 + Transpose8X16(s.Slice(-4), s.Slice(-4 + (pitch * 8)), pitch, tDst, 16); + + // Loop filtering + LpfHorizontal8Dual(tDst.Slice(4 * 16), 16, blimit0, limit0, thresh0, blimit1, limit1, thresh1); + + src[0] = tDst; + src[1] = tDst.Slice(8); + + dst[0] = s.Slice(-4); + dst[1] = s.Slice(-4 + (pitch * 8)); + + // Transpose back + Transpose(src, 16, dst, pitch, 2); + } + + public static unsafe void LpfVertical16( + ArrayPtr s, + int pitch, + ReadOnlySpan blimit, + ReadOnlySpan limit, + ReadOnlySpan thresh) + { + ulong* tDstStorage = stackalloc ulong[16]; + ArrayPtr tDst = new((byte*)tDstStorage, 16 * 8); + Span> src = stackalloc ArrayPtr[2]; + Span> dst = stackalloc ArrayPtr[2]; + + src[0] = s.Slice(-8); + src[1] = s; + dst[0] = tDst; + dst[1] = tDst.Slice(8 * 8); + + // Transpose 16x8 + Transpose(src, pitch, dst, 8, 2); + + // Loop filtering + LpfHorizontal16(tDst.Slice(8 * 8), 8, blimit, limit, thresh); + + // Transpose back + Transpose(dst, 8, src, pitch, 2); + } + + public static unsafe void LpfVertical16Dual( + ArrayPtr s, + int pitch, + ReadOnlySpan blimit, + ReadOnlySpan limit, + ReadOnlySpan thresh) + { + Vector128* tDstStorage = stackalloc Vector128[16]; + ArrayPtr tDst = new((byte*)tDstStorage, 256); + + // Transpose 16x16 + Transpose8X16(s.Slice(-8), s.Slice(-8 + (8 * pitch)), pitch, tDst, 16); + Transpose8X16(s, s.Slice(8 * pitch), pitch, tDst.Slice(8 * 16), 16); + + // Loop filtering + LpfHorizontal16Dual(tDst.Slice(8 * 16), 16, blimit, limit, thresh); + + // Transpose back + Transpose8X16(tDst, tDst.Slice(8 * 16), 16, s.Slice(-8), pitch); + Transpose8X16(tDst.Slice(8), tDst.Slice(8 + (8 * 16)), 16, s.Slice(-8 + (8 * pitch)), pitch); + } + } +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/Prob.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/Prob.cs index 4d75b35df..df82dc7a9 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/Prob.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/Prob.cs @@ -1,4 +1,4 @@ -using Ryujinx.Graphics.Nvdec.Vp9.Common; +using Ryujinx.Graphics.Nvdec.Vp9.Common; using System; using System.Diagnostics; @@ -12,10 +12,9 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp { Debug.Assert(den != 0); { - int p = (int)(((ulong)num * 256 + (den >> 1)) / den); + int p = (int)((((ulong)num * 256) + (den >> 1)) / den); // (p > 255) ? 255 : (p < 1) ? 1 : p; int clippedProb = p | ((255 - p) >> 23) | (p == 0 ? 1 : 0); - return (byte)clippedProb; } } @@ -23,14 +22,14 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp /* This function assumes prob1 and prob2 are already within [1,255] range. */ public static byte WeightedProb(int prob1, int prob2, int factor) { - return (byte)BitUtils.RoundPowerOfTwo(prob1 * (256 - factor) + prob2 * factor, 8); + return (byte)BitUtils.RoundPowerOfTwo((prob1 * (256 - factor)) + (prob2 * factor), 8); } // MODE_MV_MAX_UPDATE_FACTOR (128) * count / MODE_MV_COUNT_SAT; - private static readonly uint[] _countToUpdateFactor = { - 0, 6, 12, 19, 25, 32, 38, 44, 51, 57, 64, - 70, 76, 83, 89, 96, 102, 108, 115, 121, 128, - }; + private static readonly uint[] _countToUpdateFactor = + [ + 0, 6, 12, 19, 25, 32, 38, 44, 51, 57, 64, 70, 76, 83, 89, 96, 102, 108, 115, 121, 128 + ]; private const int ModeMvCountSat = 20; @@ -41,14 +40,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp { return preProb; } - else - { - uint count = Math.Min(den, ModeMvCountSat); - uint factor = _countToUpdateFactor[(int)count]; - byte prob = GetProb(ct0, den); - return WeightedProb(preProb, prob, (int)factor); - } + uint count = Math.Min(den, ModeMvCountSat); + uint factor = _countToUpdateFactor[(int)count]; + byte prob = GetProb(ct0, den); + return WeightedProb(preProb, prob, (int)factor); } private static uint TreeMergeProbsImpl( @@ -59,17 +55,17 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp Span probs) { int l = tree[i]; - uint leftCount = (l <= 0) ? counts[-l] : TreeMergeProbsImpl((uint)l, tree, preProbs, counts, probs); + uint leftCount = l <= 0 ? counts[-l] : TreeMergeProbsImpl((uint)l, tree, preProbs, counts, probs); int r = tree[i + 1]; - uint rightCount = (r <= 0) ? counts[-r] : TreeMergeProbsImpl((uint)r, tree, preProbs, counts, probs); + uint rightCount = r <= 0 ? counts[-r] : TreeMergeProbsImpl((uint)r, tree, preProbs, counts, probs); probs[(int)(i >> 1)] = ModeMvMergeProbs(preProbs[(int)(i >> 1)], leftCount, rightCount); - return leftCount + rightCount; } - public static void TreeMergeProbs(sbyte[] tree, ReadOnlySpan preProbs, ReadOnlySpan counts, Span probs) + public static void TreeMergeProbs(sbyte[] tree, ReadOnlySpan preProbs, ReadOnlySpan counts, + Span probs) { TreeMergeProbsImpl(0, tree, preProbs, counts, probs); } } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/Reader.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/Reader.cs index 60a20b43f..e6f34d266 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/Reader.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/Reader.cs @@ -1,4 +1,5 @@ -using Ryujinx.Common.Memory; +using Ryujinx.Common.Memory; +using Ryujinx.Graphics.Nvdec.Vp9.Types; using System; using System.Buffers.Binary; @@ -6,18 +7,18 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp { internal struct Reader { - private static readonly byte[] _norm = { - 0, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - }; + private static readonly byte[] _norm = + [ + 0, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + ]; + private const int BdValueSize = sizeof(ulong) * 8; // This is meant to be a large, positive constant that can still be efficiently @@ -36,16 +37,13 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp { return true; } - else - { - _buffer = new ArrayPtr(ref buffer[0], size); - Value = 0; - Count = -8; - Range = 255; - Fill(); - return ReadBit() != 0; // Marker bit - } + _buffer = new ArrayPtr(ref buffer[0], size); + Value = 0; + Count = -8; + Range = 255; + Fill(); + return ReadBit() != 0; // Marker bit } private void Fill() @@ -65,7 +63,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp ulong bigEndianValues = BinaryPrimitives.ReadUInt64BigEndian(buffer); nv = bigEndianValues >> (BdValueSize - bits); count += bits; - buffer = buffer[(bits >> 3)..]; + buffer = buffer.Slice(bits >> 3); value = Value | (nv << (shift & 0x7)); } else @@ -84,7 +82,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp { count += 8; value |= (ulong)buffer[0] << shift; - buffer = buffer[1..]; + buffer = buffer.Slice(1); shift -= 8; } } @@ -98,7 +96,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp Count = count; } - public readonly bool HasError() + public bool HasError() { // Check if we have reached the end of the buffer. // @@ -124,7 +122,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp ulong bigsplit; int count; uint range; - uint split = (Range * (uint)prob + (256 - (uint)prob)) >> 8; + uint split = ((Range * (uint)prob) + (256 - (uint)prob)) >> 8; if (Count < 0) { @@ -188,7 +186,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp public int ReadBool(int prob, ref ulong value, ref int count, ref uint range) { - uint split = (range * (uint)prob + (256 - (uint)prob)) >> 8; + uint split = ((range * (uint)prob) + (256 - (uint)prob)) >> 8; ulong bigsplit = (ulong)split << (BdValueSize - 8); if (count < 0) @@ -202,8 +200,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp if (value >= bigsplit) { - range -= split; - value -= bigsplit; + range = range - split; + value = value - bigsplit; { int shift = _norm[range]; range <<= shift; @@ -212,6 +210,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp } return 1; } + range = split; { int shift = _norm[range]; @@ -230,7 +229,82 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp Count -= 8; _buffer = _buffer.Slice(-1); } + return _buffer; } + + private int DecodeUniform() + { + const int L = 8; + const int M = (1 << L) - 191; + int v = ReadLiteral(L - 1); + return v < M ? v : (v << 1) - M + ReadBit(); + } + + public int DecodeTermSubexp() + { + if (ReadBit() == 0) + { + return ReadLiteral(4); + } + + if (ReadBit() == 0) + { + return ReadLiteral(4) + 16; + } + + if (ReadBit() == 0) + { + return ReadLiteral(5) + 32; + } + + return DecodeUniform() + 64; + } + + public TxMode ReadTxMode() + { + TxMode txMode = (TxMode)ReadLiteral(2); + if (txMode == TxMode.Allow32X32) + { + txMode += ReadBit(); + } + + return txMode; + } + + public int ReadCoeff( + ReadOnlySpan probs, + int n, + ref ulong value, + ref int count, + ref uint range) + { + int val = 0; + for (int i = 0; i < n; ++i) + { + val = (val << 1) | ReadBool(probs[i], ref value, ref count, ref range); + } + + return val; + } + + public void DiffUpdateProb(ref byte p) + { + if (Read(Entropy.DiffUpdateProb) != 0) + { + p = (byte)DSubExp.InvRemapProb(DecodeTermSubexp(), p); + } + } + + public void UpdateMvProbs(Span p, int n) + { + for (int i = 0; i < n; ++i) + { + if (Read(EntropyMv.UpdateProb) != 0) + { + p[i] = (byte)((ReadLiteral(7) << 1) | 1); + } + } + } } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/TxfmCommon.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/TxfmCommon.cs index 209f19510..d84c0bb16 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/TxfmCommon.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/TxfmCommon.cs @@ -13,42 +13,42 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp // for (int i = 1; i < 32; ++i) // Console.WriteLine("public const short CosPi{0}_64 = {1};", i, MathF.Round(16384 * MathF.Cos(i * MathF.PI / 64))); // Note: sin(k * Pi / 64) = cos((32 - k) * Pi / 64) - public const short CosPi1_64 = 16364; - public const short CosPi2_64 = 16305; - public const short CosPi3_64 = 16207; - public const short CosPi4_64 = 16069; - public const short CosPi5_64 = 15893; - public const short CosPi6_64 = 15679; - public const short CosPi7_64 = 15426; - public const short CosPi8_64 = 15137; - public const short CosPi9_64 = 14811; - public const short CosPi10_64 = 14449; - public const short CosPi11_64 = 14053; - public const short CosPi12_64 = 13623; - public const short CosPi13_64 = 13160; - public const short CosPi14_64 = 12665; - public const short CosPi15_64 = 12140; - public const short CosPi16_64 = 11585; - public const short CosPi17_64 = 11003; - public const short CosPi18_64 = 10394; - public const short CosPi19_64 = 9760; - public const short CosPi20_64 = 9102; - public const short CosPi21_64 = 8423; - public const short CosPi22_64 = 7723; - public const short CosPi23_64 = 7005; - public const short CosPi24_64 = 6270; - public const short CosPi25_64 = 5520; - public const short CosPi26_64 = 4756; - public const short CosPi27_64 = 3981; - public const short CosPi28_64 = 3196; - public const short CosPi29_64 = 2404; - public const short CosPi30_64 = 1606; - public const short CosPi31_64 = 804; + public const short CosPi164 = 16364; + public const short CosPi264 = 16305; + public const short CosPi364 = 16207; + public const short CosPi464 = 16069; + public const short CosPi564 = 15893; + public const short CosPi664 = 15679; + public const short CosPi764 = 15426; + public const short CosPi864 = 15137; + public const short CosPi964 = 14811; + public const short CosPi1064 = 14449; + public const short CosPi1164 = 14053; + public const short CosPi1264 = 13623; + public const short CosPi1364 = 13160; + public const short CosPi1464 = 12665; + public const short CosPi1564 = 12140; + public const short CosPi1664 = 11585; + public const short CosPi1764 = 11003; + public const short CosPi1864 = 10394; + public const short CosPi1964 = 9760; + public const short CosPi2064 = 9102; + public const short CosPi2164 = 8423; + public const short CosPi2264 = 7723; + public const short CosPi2364 = 7005; + public const short CosPi2464 = 6270; + public const short CosPi2564 = 5520; + public const short CosPi2664 = 4756; + public const short CosPi2764 = 3981; + public const short CosPi2864 = 3196; + public const short CosPi2964 = 2404; + public const short CosPi3064 = 1606; + public const short CosPi3164 = 804; // 16384 * sqrt(2) * sin(kPi / 9) * 2 / 3 - public const short SinPi1_9 = 5283; - public const short SinPi2_9 = 9929; - public const short SinPi3_9 = 13377; - public const short SinPi4_9 = 15212; + public const short SinPi19 = 5283; + public const short SinPi29 = 9929; + public const short SinPi39 = 13377; + public const short SinPi49 = 15212; } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Entropy.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Entropy.cs new file mode 100644 index 000000000..380879453 --- /dev/null +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Entropy.cs @@ -0,0 +1,623 @@ +using Ryujinx.Graphics.Nvdec.Vp9.Types; +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Ryujinx.Graphics.Nvdec.Vp9 +{ + internal static class Entropy + { + public const int DiffUpdateProb = 252; + + // Coefficient token alphabet + public const int ZeroToken = 0; // 0 Extra Bits 0+0 + public const int OneToken = 1; // 1 Extra Bits 0+1 + public const int TwoToken = 2; // 2 Extra Bits 0+1 + public const int ThreeToken = 3; // 3 Extra Bits 0+1 + public const int FourToken = 4; // 4 Extra Bits 0+1 + public const int Category1Token = 5; // 5-6 Extra Bits 1+1 + public const int Category2Token = 6; // 7-10 Extra Bits 2+1 + public const int Category3Token = 7; // 11-18 Extra Bits 3+1 + public const int Category4Token = 8; // 19-34 Extra Bits 4+1 + public const int Category5Token = 9; // 35-66 Extra Bits 5+1 + public const int Category6Token = 10; // 67+ Extra Bits 14+1 + public const int EobToken = 11; // EOB Extra Bits 0+0 + + public const int EntropyTokens = 12; + + public const int RefTypes = 2; // intra=0, inter=1 + + /* Middle dimension reflects the coefficient position within the transform. */ + public const int CoefBands = 6; + + /* Inside dimension is measure of nearby complexity, that reflects the energy + of nearby coefficients are nonzero. For the first coefficient (DC, unless + block type is 0), we look at the (already encoded) blocks above and to the + left of the current block. The context index is then the number (0,1,or 2) + of these blocks having nonzero coefficients. + After decoding a coefficient, the measure is determined by the size of the + most recently decoded coefficient. + Note that the intuitive meaning of this measure changes as coefficients + are decoded, e.g., prior to the first token, a zero means that my neighbors + are empty while, after the first token, because of the use of end-of-block, + a zero means we just decoded a zero and hence guarantees that a non-zero + coefficient will appear later in this block. However, this shift + in meaning is perfectly OK because our context depends also on the + coefficient band (and since zigzag positions 0, 1, and 2 are in + distinct bands). */ + + public const int CoeffContexts = 6; + + public static int BAND_COEFF_CONTEXTS(int band) + { + return band == 0 ? 3 : CoeffContexts; + } + + public const int UnconstrainedNodes = 3; + + public const int PivotNode = 2; + + public const int Cat1MinVal = 5; + public const int Cat2MinVal = 7; + public const int Cat3MinVal = 11; + public const int Cat4MinVal = 19; + public const int Cat5MinVal = 35; + public const int Cat6MinVal = 67; + + public static readonly byte[] Cat1Prob = [159]; + public static readonly byte[] Cat2Prob = [165, 145]; + public static readonly byte[] Cat3Prob = [173, 148, 140]; + public static readonly byte[] Cat4Prob = [176, 155, 140, 135]; + public static readonly byte[] Cat5Prob = [180, 157, 141, 134, 130]; + + public static readonly byte[] Cat6Prob = + [ + 254, 254, 254, 252, 249, 243, 230, 196, 177, 153, 140, 133, 130, 129 + ]; + + public static readonly byte[] Cat6ProbHigh12 = + [ + 255, 255, 255, 255, 254, 254, 54, 252, 249, 243, 230, 196, 177, 153, 140, 133, 130, 129 + ]; + + public const int EobModelToken = 3; + + private static readonly byte[] _coefbandTrans8X8Plus = + [ + 0, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, + // beyond MAXBAND_INDEX+1 all values are filled as 5 + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 + ]; + + private static readonly byte[] _coefbandTrans4X4 = [0, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 5, 5, 5]; + + public static readonly byte[][] Pareto8Full = + [ + [3, 86, 128, 6, 86, 23, 88, 29], [6, 86, 128, 11, 87, 42, 91, 52], + [9, 86, 129, 17, 88, 61, 94, 76], [12, 86, 129, 22, 88, 77, 97, 93], + [15, 87, 129, 28, 89, 93, 100, 110], [17, 87, 129, 33, 90, 105, 103, 123], + [20, 88, 130, 38, 91, 118, 106, 136], [23, 88, 130, 43, 91, 128, 108, 146], + [26, 89, 131, 48, 92, 139, 111, 156], [28, 89, 131, 53, 93, 147, 114, 163], + [31, 90, 131, 58, 94, 156, 117, 171], [34, 90, 131, 62, 94, 163, 119, 177], + [37, 90, 132, 66, 95, 171, 122, 184], [39, 90, 132, 70, 96, 177, 124, 189], + [42, 91, 132, 75, 97, 183, 127, 194], [44, 91, 132, 79, 97, 188, 129, 198], + [47, 92, 133, 83, 98, 193, 132, 202], [49, 92, 133, 86, 99, 197, 134, 205], + [52, 93, 133, 90, 100, 201, 137, 208], [54, 93, 133, 94, 100, 204, 139, 211], + [57, 94, 134, 98, 101, 208, 142, 214], [59, 94, 134, 101, 102, 211, 144, 216], + [62, 94, 135, 105, 103, 214, 146, 218], + [64, 94, 135, 108, 103, 216, 148, 220], + [66, 95, 135, 111, 104, 219, 151, 222], + [68, 95, 135, 114, 105, 221, 153, 223], + [71, 96, 136, 117, 106, 224, 155, 225], + [73, 96, 136, 120, 106, 225, 157, 226], + [76, 97, 136, 123, 107, 227, 159, 228], + [78, 97, 136, 126, 108, 229, 160, 229], + [80, 98, 137, 129, 109, 231, 162, 231], + [82, 98, 137, 131, 109, 232, 164, 232], + [84, 98, 138, 134, 110, 234, 166, 233], + [86, 98, 138, 137, 111, 235, 168, 234], + [89, 99, 138, 140, 112, 236, 170, 235], + [91, 99, 138, 142, 112, 237, 171, 235], + [93, 100, 139, 145, 113, 238, 173, 236], + [95, 100, 139, 147, 114, 239, 174, 237], + [97, 101, 140, 149, 115, 240, 176, 238], + [99, 101, 140, 151, 115, 241, 177, 238], + [101, 102, 140, 154, 116, 242, 179, 239], + [103, 102, 140, 156, 117, 242, 180, 239], + [105, 103, 141, 158, 118, 243, 182, 240], + [107, 103, 141, 160, 118, 243, 183, 240], + [109, 104, 141, 162, 119, 244, 185, 241], + [111, 104, 141, 164, 119, 244, 186, 241], + [113, 104, 142, 166, 120, 245, 187, 242], + [114, 104, 142, 168, 121, 245, 188, 242], + [116, 105, 143, 170, 122, 246, 190, 243], + [118, 105, 143, 171, 122, 246, 191, 243], + [120, 106, 143, 173, 123, 247, 192, 244], + [121, 106, 143, 175, 124, 247, 193, 244], + [123, 107, 144, 177, 125, 248, 195, 244], + [125, 107, 144, 178, 125, 248, 196, 244], + [127, 108, 145, 180, 126, 249, 197, 245], + [128, 108, 145, 181, 127, 249, 198, 245], + [130, 109, 145, 183, 128, 249, 199, 245], + [132, 109, 145, 184, 128, 249, 200, 245], + [134, 110, 146, 186, 129, 250, 201, 246], + [135, 110, 146, 187, 130, 250, 202, 246], + [137, 111, 147, 189, 131, 251, 203, 246], + [138, 111, 147, 190, 131, 251, 204, 246], + [140, 112, 147, 192, 132, 251, 205, 247], + [141, 112, 147, 193, 132, 251, 206, 247], + [143, 113, 148, 194, 133, 251, 207, 247], + [144, 113, 148, 195, 134, 251, 207, 247], + [146, 114, 149, 197, 135, 252, 208, 248], + [147, 114, 149, 198, 135, 252, 209, 248], + [149, 115, 149, 199, 136, 252, 210, 248], + [150, 115, 149, 200, 137, 252, 210, 248], + [152, 115, 150, 201, 138, 252, 211, 248], + [153, 115, 150, 202, 138, 252, 212, 248], + [155, 116, 151, 204, 139, 253, 213, 249], + [156, 116, 151, 205, 139, 253, 213, 249], + [158, 117, 151, 206, 140, 253, 214, 249], + [159, 117, 151, 207, 141, 253, 215, 249], + [161, 118, 152, 208, 142, 253, 216, 249], + [162, 118, 152, 209, 142, 253, 216, 249], + [163, 119, 153, 210, 143, 253, 217, 249], + [164, 119, 153, 211, 143, 253, 217, 249], + [166, 120, 153, 212, 144, 254, 218, 250], + [167, 120, 153, 212, 145, 254, 219, 250], + [168, 121, 154, 213, 146, 254, 220, 250], + [169, 121, 154, 214, 146, 254, 220, 250], + [171, 122, 155, 215, 147, 254, 221, 250], + [172, 122, 155, 216, 147, 254, 221, 250], + [173, 123, 155, 217, 148, 254, 222, 250], + [174, 123, 155, 217, 149, 254, 222, 250], + [176, 124, 156, 218, 150, 254, 223, 250], + [177, 124, 156, 219, 150, 254, 223, 250], + [178, 125, 157, 220, 151, 254, 224, 251], + [179, 125, 157, 220, 151, 254, 224, 251], + [180, 126, 157, 221, 152, 254, 225, 251], + [181, 126, 157, 221, 152, 254, 225, 251], + [183, 127, 158, 222, 153, 254, 226, 251], + [184, 127, 158, 223, 154, 254, 226, 251], + [185, 128, 159, 224, 155, 255, 227, 251], + [186, 128, 159, 224, 155, 255, 227, 251], + [187, 129, 160, 225, 156, 255, 228, 251], + [188, 130, 160, 225, 156, 255, 228, 251], + [189, 131, 160, 226, 157, 255, 228, 251], + [190, 131, 160, 226, 158, 255, 228, 251], + [191, 132, 161, 227, 159, 255, 229, 251], + [192, 132, 161, 227, 159, 255, 229, 251], + [193, 133, 162, 228, 160, 255, 230, 252], + [194, 133, 162, 229, 160, 255, 230, 252], + [195, 134, 163, 230, 161, 255, 231, 252], + [196, 134, 163, 230, 161, 255, 231, 252], + [197, 135, 163, 231, 162, 255, 231, 252], + [198, 135, 163, 231, 162, 255, 231, 252], + [199, 136, 164, 232, 163, 255, 232, 252], + [200, 136, 164, 232, 164, 255, 232, 252], + [201, 137, 165, 233, 165, 255, 233, 252], + [201, 137, 165, 233, 165, 255, 233, 252], + [202, 138, 166, 233, 166, 255, 233, 252], + [203, 138, 166, 233, 166, 255, 233, 252], + [204, 139, 166, 234, 167, 255, 234, 252], + [205, 139, 166, 234, 167, 255, 234, 252], + [206, 140, 167, 235, 168, 255, 235, 252], + [206, 140, 167, 235, 168, 255, 235, 252], + [207, 141, 168, 236, 169, 255, 235, 252], + [208, 141, 168, 236, 170, 255, 235, 252], + [209, 142, 169, 237, 171, 255, 236, 252], + [209, 143, 169, 237, 171, 255, 236, 252], + [210, 144, 169, 237, 172, 255, 236, 252], + [211, 144, 169, 237, 172, 255, 236, 252], + [212, 145, 170, 238, 173, 255, 237, 252], + [213, 145, 170, 238, 173, 255, 237, 252], + [214, 146, 171, 239, 174, 255, 237, 253], + [214, 146, 171, 239, 174, 255, 237, 253], + [215, 147, 172, 240, 175, 255, 238, 253], + [215, 147, 172, 240, 175, 255, 238, 253], + [216, 148, 173, 240, 176, 255, 238, 253], + [217, 148, 173, 240, 176, 255, 238, 253], + [218, 149, 173, 241, 177, 255, 239, 253], + [218, 149, 173, 241, 178, 255, 239, 253], + [219, 150, 174, 241, 179, 255, 239, 253], + [219, 151, 174, 241, 179, 255, 239, 253], + [220, 152, 175, 242, 180, 255, 240, 253], + [221, 152, 175, 242, 180, 255, 240, 253], + [222, 153, 176, 242, 181, 255, 240, 253], + [222, 153, 176, 242, 181, 255, 240, 253], + [223, 154, 177, 243, 182, 255, 240, 253], + [223, 154, 177, 243, 182, 255, 240, 253], + [224, 155, 178, 244, 183, 255, 241, 253], + [224, 155, 178, 244, 183, 255, 241, 253], + [225, 156, 178, 244, 184, 255, 241, 253], + [225, 157, 178, 244, 184, 255, 241, 253], + [226, 158, 179, 244, 185, 255, 242, 253], + [227, 158, 179, 244, 185, 255, 242, 253], + [228, 159, 180, 245, 186, 255, 242, 253], + [228, 159, 180, 245, 186, 255, 242, 253], + [229, 160, 181, 245, 187, 255, 242, 253], + [229, 160, 181, 245, 187, 255, 242, 253], + [230, 161, 182, 246, 188, 255, 243, 253], + [230, 162, 182, 246, 188, 255, 243, 253], + [231, 163, 183, 246, 189, 255, 243, 253], + [231, 163, 183, 246, 189, 255, 243, 253], + [232, 164, 184, 247, 190, 255, 243, 253], + [232, 164, 184, 247, 190, 255, 243, 253], + [233, 165, 185, 247, 191, 255, 244, 253], + [233, 165, 185, 247, 191, 255, 244, 253], + [234, 166, 185, 247, 192, 255, 244, 253], + [234, 167, 185, 247, 192, 255, 244, 253], + [235, 168, 186, 248, 193, 255, 244, 253], + [235, 168, 186, 248, 193, 255, 244, 253], + [236, 169, 187, 248, 194, 255, 244, 253], + [236, 169, 187, 248, 194, 255, 244, 253], + [236, 170, 188, 248, 195, 255, 245, 253], + [236, 170, 188, 248, 195, 255, 245, 253], + [237, 171, 189, 249, 196, 255, 245, 254], + [237, 172, 189, 249, 196, 255, 245, 254], + [238, 173, 190, 249, 197, 255, 245, 254], + [238, 173, 190, 249, 197, 255, 245, 254], + [239, 174, 191, 249, 198, 255, 245, 254], + [239, 174, 191, 249, 198, 255, 245, 254], + [240, 175, 192, 249, 199, 255, 246, 254], + [240, 176, 192, 249, 199, 255, 246, 254], + [240, 177, 193, 250, 200, 255, 246, 254], + [240, 177, 193, 250, 200, 255, 246, 254], + [241, 178, 194, 250, 201, 255, 246, 254], + [241, 178, 194, 250, 201, 255, 246, 254], + [242, 179, 195, 250, 202, 255, 246, 254], + [242, 180, 195, 250, 202, 255, 246, 254], + [242, 181, 196, 250, 203, 255, 247, 254], + [242, 181, 196, 250, 203, 255, 247, 254], + [243, 182, 197, 251, 204, 255, 247, 254], + [243, 183, 197, 251, 204, 255, 247, 254], + [244, 184, 198, 251, 205, 255, 247, 254], + [244, 184, 198, 251, 205, 255, 247, 254], + [244, 185, 199, 251, 206, 255, 247, 254], + [244, 185, 199, 251, 206, 255, 247, 254], + [245, 186, 200, 251, 207, 255, 247, 254], + [245, 187, 200, 251, 207, 255, 247, 254], + [246, 188, 201, 252, 207, 255, 248, 254], + [246, 188, 201, 252, 207, 255, 248, 254], + [246, 189, 202, 252, 208, 255, 248, 254], + [246, 190, 202, 252, 208, 255, 248, 254], + [247, 191, 203, 252, 209, 255, 248, 254], + [247, 191, 203, 252, 209, 255, 248, 254], + [247, 192, 204, 252, 210, 255, 248, 254], + [247, 193, 204, 252, 210, 255, 248, 254], + [248, 194, 205, 252, 211, 255, 248, 254], + [248, 194, 205, 252, 211, 255, 248, 254], + [248, 195, 206, 252, 212, 255, 249, 254], + [248, 196, 206, 252, 212, 255, 249, 254], + [249, 197, 207, 253, 213, 255, 249, 254], + [249, 197, 207, 253, 213, 255, 249, 254], + [249, 198, 208, 253, 214, 255, 249, 254], + [249, 199, 209, 253, 214, 255, 249, 254], + [250, 200, 210, 253, 215, 255, 249, 254], + [250, 200, 210, 253, 215, 255, 249, 254], + [250, 201, 211, 253, 215, 255, 249, 254], + [250, 202, 211, 253, 215, 255, 249, 254], + [250, 203, 212, 253, 216, 255, 249, 254], + [250, 203, 212, 253, 216, 255, 249, 254], + [251, 204, 213, 253, 217, 255, 250, 254], + [251, 205, 213, 253, 217, 255, 250, 254], + [251, 206, 214, 254, 218, 255, 250, 254], + [251, 206, 215, 254, 218, 255, 250, 254], + [252, 207, 216, 254, 219, 255, 250, 254], + [252, 208, 216, 254, 219, 255, 250, 254], + [252, 209, 217, 254, 220, 255, 250, 254], + [252, 210, 217, 254, 220, 255, 250, 254], + [252, 211, 218, 254, 221, 255, 250, 254], + [252, 212, 218, 254, 221, 255, 250, 254], + [253, 213, 219, 254, 222, 255, 250, 254], + [253, 213, 220, 254, 222, 255, 250, 254], + [253, 214, 221, 254, 223, 255, 250, 254], + [253, 215, 221, 254, 223, 255, 250, 254], + [253, 216, 222, 254, 224, 255, 251, 254], + [253, 217, 223, 254, 224, 255, 251, 254], + [253, 218, 224, 254, 225, 255, 251, 254], + [253, 219, 224, 254, 225, 255, 251, 254], + [254, 220, 225, 254, 225, 255, 251, 254], + [254, 221, 226, 254, 225, 255, 251, 254], + [254, 222, 227, 255, 226, 255, 251, 254], + [254, 223, 227, 255, 226, 255, 251, 254], + [254, 224, 228, 255, 227, 255, 251, 254], + [254, 225, 229, 255, 227, 255, 251, 254], + [254, 226, 230, 255, 228, 255, 251, 254], + [254, 227, 230, 255, 229, 255, 251, 254], + [255, 228, 231, 255, 230, 255, 251, 254], + [255, 229, 232, 255, 230, 255, 251, 254], + [255, 230, 233, 255, 231, 255, 252, 254], + [255, 231, 234, 255, 231, 255, 252, 254], + [255, 232, 235, 255, 232, 255, 252, 254], + [255, 233, 236, 255, 232, 255, 252, 254], + [255, 235, 237, 255, 233, 255, 252, 254], + [255, 236, 238, 255, 234, 255, 252, 254], + [255, 238, 240, 255, 235, 255, 252, 255], + [255, 239, 241, 255, 235, 255, 252, 254], + [255, 241, 243, 255, 236, 255, 252, 254], + [255, 243, 245, 255, 237, 255, 252, 254], + [255, 246, 247, 255, 239, 255, 253, 255] + ]; + + internal static readonly byte[] DefaultCoefProbs4X4 = + [ + // Y plane + // Intra + // Band 0 + 195, 29, 183, 84, 49, 136, 8, 42, 71, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // Band 1 + 31, 107, 169, 35, 99, 159, 17, 82, 140, 8, 66, 114, 2, 44, 76, 1, 19, 32, + // Band 2 + 40, 132, 201, 29, 114, 187, 13, 91, 157, 7, 75, 127, 3, 58, 95, 1, 28, 47, + // Band 3 + 69, 142, 221, 42, 122, 201, 15, 91, 159, 6, 67, 121, 1, 42, 77, 1, 17, 31, + // Band 4 + 102, 148, 228, 67, 117, 204, 17, 82, 154, 6, 59, 114, 2, 39, 75, 1, 15, 29, + // Band 5 + 156, 57, 233, 119, 57, 212, 58, 48, 163, 29, 40, 124, 12, 30, 81, 3, 12, 31, + // Inter + // Band 0 + 191, 107, 226, 124, 117, 204, 25, 99, 155, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // Band 1 + 29, 148, 210, 37, 126, 194, 8, 93, 157, 2, 68, 118, 1, 39, 69, 1, 17, 33, + // Band 2 + 41, 151, 213, 27, 123, 193, 3, 82, 144, 1, 58, 105, 1, 32, 60, 1, 13, 26, + // Band 3 + 59, 159, 220, 23, 126, 198, 4, 88, 151, 1, 66, 114, 1, 38, 71, 1, 18, 34, + // Band 4 + 114, 136, 232, 51, 114, 207, 11, 83, 155, 3, 56, 105, 1, 33, 65, 1, 17, 34, + // Band 5 + 149, 65, 234, 121, 57, 215, 61, 49, 166, 28, 36, 114, 12, 25, 76, 3, 16, 42, + // UV plane + // Intra + // Band 0 + 214, 49, 220, 132, 63, 188, 42, 65, 137, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // Band 1 + 85, 137, 221, 104, 131, 216, 49, 111, 192, 21, 87, 155, 2, 49, 87, 1, 16, 28, + // Band 2 + 89, 163, 230, 90, 137, 220, 29, 100, 183, 10, 70, 135, 2, 42, 81, 1, 17, 33, + // Band 3 + 108, 167, 237, 55, 133, 222, 15, 97, 179, 4, 72, 135, 1, 45, 85, 1, 19, 38, + // Band 4 + 124, 146, 240, 66, 124, 224, 17, 88, 175, 4, 58, 122, 1, 36, 75, 1, 18, 37, + // Band 5 + 141, 79, 241, 126, 70, 227, 66, 58, 182, 30, 44, 136, 12, 34, 96, 2, 20, 47, + // Inter + // Band 0 + 229, 99, 249, 143, 111, 235, 46, 109, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // Band 1 + 82, 158, 236, 94, 146, 224, 25, 117, 191, 9, 87, 149, 3, 56, 99, 1, 33, 57, + // Band 2 + 83, 167, 237, 68, 145, 222, 10, 103, 177, 2, 72, 131, 1, 41, 79, 1, 20, 39, + // Band 3 + 99, 167, 239, 47, 141, 224, 10, 104, 178, 2, 73, 133, 1, 44, 85, 1, 22, 47, + // Band 4 + 127, 145, 243, 71, 129, 228, 17, 93, 177, 3, 61, 124, 1, 41, 84, 1, 21, 52, + // Band 5 + 157, 78, 244, 140, 72, 231, 69, 58, 184, 31, 44, 137, 14, 38, 105, 8, 23, 61 + ]; + + internal static readonly byte[] DefaultCoefProbs8X8 = + [ + // Y plane + // Intra + // Band 0 + 125, 34, 187, 52, 41, 133, 6, 31, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // Band 1 + 37, 109, 153, 51, 102, 147, 23, 87, 128, 8, 67, 101, 1, 41, 63, 1, 19, 29, + // Band 2 + 31, 154, 185, 17, 127, 175, 6, 96, 145, 2, 73, 114, 1, 51, 82, 1, 28, 45, + // Band 3 + 23, 163, 200, 10, 131, 185, 2, 93, 148, 1, 67, 111, 1, 41, 69, 1, 14, 24, + // Band 4 + 29, 176, 217, 12, 145, 201, 3, 101, 156, 1, 69, 111, 1, 39, 63, 1, 14, 23, + // Band 5 + 57, 192, 233, 25, 154, 215, 6, 109, 167, 3, 78, 118, 1, 48, 69, 1, 21, 29, + // Inter + // Band 0 + 202, 105, 245, 108, 106, 216, 18, 90, 144, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // Band 1 + 33, 172, 219, 64, 149, 206, 14, 117, 177, 5, 90, 141, 2, 61, 95, 1, 37, 57, + // Band 2 + 33, 179, 220, 11, 140, 198, 1, 89, 148, 1, 60, 104, 1, 33, 57, 1, 12, 21, + // Band 3 + 30, 181, 221, 8, 141, 198, 1, 87, 145, 1, 58, 100, 1, 31, 55, 1, 12, 20, + // Band 4 + 32, 186, 224, 7, 142, 198, 1, 86, 143, 1, 58, 100, 1, 31, 55, 1, 12, 22, + // Band 5 + 57, 192, 227, 20, 143, 204, 3, 96, 154, 1, 68, 112, 1, 42, 69, 1, 19, 32, + // UV plane + // Intra + // Band 0 + 212, 35, 215, 113, 47, 169, 29, 48, 105, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // Band 1 + 74, 129, 203, 106, 120, 203, 49, 107, 178, 19, 84, 144, 4, 50, 84, 1, 15, 25, + // Band 2 + 71, 172, 217, 44, 141, 209, 15, 102, 173, 6, 76, 133, 2, 51, 89, 1, 24, 42, + // Band 3 + 64, 185, 231, 31, 148, 216, 8, 103, 175, 3, 74, 131, 1, 46, 81, 1, 18, 30, + // Band 4 + 65, 196, 235, 25, 157, 221, 5, 105, 174, 1, 67, 120, 1, 38, 69, 1, 15, 30, + // Band 5 + 65, 204, 238, 30, 156, 224, 7, 107, 177, 2, 70, 124, 1, 42, 73, 1, 18, 34, + // Inter + // Band 0 + 225, 86, 251, 144, 104, 235, 42, 99, 181, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // Band 1 + 85, 175, 239, 112, 165, 229, 29, 136, 200, 12, 103, 162, 6, 77, 123, 2, 53, 84, + // Band 2 + 75, 183, 239, 30, 155, 221, 3, 106, 171, 1, 74, 128, 1, 44, 76, 1, 17, 28, + // Band 3 + 73, 185, 240, 27, 159, 222, 2, 107, 172, 1, 75, 127, 1, 42, 73, 1, 17, 29, + // Band 4 + 62, 190, 238, 21, 159, 222, 2, 107, 172, 1, 72, 122, 1, 40, 71, 1, 18, 32, + // Band 5 + 61, 199, 240, 27, 161, 226, 4, 113, 180, 1, 76, 129, 1, 46, 80, 1, 23, 41 + ]; + + internal static readonly byte[] DefaultCoefProbs16X16 = + [ + // Y plane + // Intra + // Band 0 + 7, 27, 153, 5, 30, 95, 1, 16, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // Band 1 + 50, 75, 127, 57, 75, 124, 27, 67, 108, 10, 54, 86, 1, 33, 52, 1, 12, 18, + // Band 2 + 43, 125, 151, 26, 108, 148, 7, 83, 122, 2, 59, 89, 1, 38, 60, 1, 17, 27, + // Band 3 + 23, 144, 163, 13, 112, 154, 2, 75, 117, 1, 50, 81, 1, 31, 51, 1, 14, 23, + // Band 4 + 18, 162, 185, 6, 123, 171, 1, 78, 125, 1, 51, 86, 1, 31, 54, 1, 14, 23, + // Band 5 + 15, 199, 227, 3, 150, 204, 1, 91, 146, 1, 55, 95, 1, 30, 53, 1, 11, 20, + // Inter + // Band 0 + 19, 55, 240, 19, 59, 196, 3, 52, 105, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // Band 1 + 41, 166, 207, 104, 153, 199, 31, 123, 181, 14, 101, 152, 5, 72, 106, 1, 36, 52, + // Band 2 + 35, 176, 211, 12, 131, 190, 2, 88, 144, 1, 60, 101, 1, 36, 60, 1, 16, 28, + // Band 3 + 28, 183, 213, 8, 134, 191, 1, 86, 142, 1, 56, 96, 1, 30, 53, 1, 12, 20, + // Band 4 + 20, 190, 215, 4, 135, 192, 1, 84, 139, 1, 53, 91, 1, 28, 49, 1, 11, 20, + // Band 5 + 13, 196, 216, 2, 137, 192, 1, 86, 143, 1, 57, 99, 1, 32, 56, 1, 13, 24, + // UV plane + // Intra + // Band 0 + 211, 29, 217, 96, 47, 156, 22, 43, 87, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // Band 1 + 78, 120, 193, 111, 116, 186, 46, 102, 164, 15, 80, 128, 2, 49, 76, 1, 18, 28, + // Band 2 + 71, 161, 203, 42, 132, 192, 10, 98, 150, 3, 69, 109, 1, 44, 70, 1, 18, 29, + // Band 3 + 57, 186, 211, 30, 140, 196, 4, 93, 146, 1, 62, 102, 1, 38, 65, 1, 16, 27, + // Band 4 + 47, 199, 217, 14, 145, 196, 1, 88, 142, 1, 57, 98, 1, 36, 62, 1, 15, 26, + // Band 5 + 26, 219, 229, 5, 155, 207, 1, 94, 151, 1, 60, 104, 1, 36, 62, 1, 16, 28, + // Inter + // Band 0 + 233, 29, 248, 146, 47, 220, 43, 52, 140, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // Band 1 + 100, 163, 232, 179, 161, 222, 63, 142, 204, 37, 113, 174, 26, 89, 137, 18, 68, 97, + // Band 2 + 85, 181, 230, 32, 146, 209, 7, 100, 164, 3, 71, 121, 1, 45, 77, 1, 18, 30, + // Band 3 + 65, 187, 230, 20, 148, 207, 2, 97, 159, 1, 68, 116, 1, 40, 70, 1, 14, 29, + // Band 4 + 40, 194, 227, 8, 147, 204, 1, 94, 155, 1, 65, 112, 1, 39, 66, 1, 14, 26, + // Band 5 + 16, 208, 228, 3, 151, 207, 1, 98, 160, 1, 67, 117, 1, 41, 74, 1, 17, 31 + ]; + + internal static readonly byte[] DefaultCoefProbs32X32 = + [ + // Y plane + // Intra + // Band 0 + 17, 38, 140, 7, 34, 80, 1, 17, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // Band 1 + 37, 75, 128, 41, 76, 128, 26, 66, 116, 12, 52, 94, 2, 32, 55, 1, 10, 16, + // Band 2 + 50, 127, 154, 37, 109, 152, 16, 82, 121, 5, 59, 85, 1, 35, 54, 1, 13, 20, + // Band 3 + 40, 142, 167, 17, 110, 157, 2, 71, 112, 1, 44, 72, 1, 27, 45, 1, 11, 17, + // Band 4 + 30, 175, 188, 9, 124, 169, 1, 74, 116, 1, 48, 78, 1, 30, 49, 1, 11, 18, + // Band 5 + 10, 222, 223, 2, 150, 194, 1, 83, 128, 1, 48, 79, 1, 27, 45, 1, 11, 17, + // Inter + // Band 0 + 36, 41, 235, 29, 36, 193, 10, 27, 111, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // Band 1 + 85, 165, 222, 177, 162, 215, 110, 135, 195, 57, 113, 168, 23, 83, 120, 10, 49, 61, + // Band 2 + 85, 190, 223, 36, 139, 200, 5, 90, 146, 1, 60, 103, 1, 38, 65, 1, 18, 30, + // Band 3 + 72, 202, 223, 23, 141, 199, 2, 86, 140, 1, 56, 97, 1, 36, 61, 1, 16, 27, + // Band 4 + 55, 218, 225, 13, 145, 200, 1, 86, 141, 1, 57, 99, 1, 35, 61, 1, 13, 22, + // Band 5 + 15, 235, 212, 1, 132, 184, 1, 84, 139, 1, 57, 97, 1, 34, 56, 1, 14, 23, + // UV plane + // Intra + // Band 0 + 181, 21, 201, 61, 37, 123, 10, 38, 71, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // Band 1 + 47, 106, 172, 95, 104, 173, 42, 93, 159, 18, 77, 131, 4, 50, 81, 1, 17, 23, + // Band 2 + 62, 147, 199, 44, 130, 189, 28, 102, 154, 18, 75, 115, 2, 44, 65, 1, 12, 19, + // Band 3 + 55, 153, 210, 24, 130, 194, 3, 93, 146, 1, 61, 97, 1, 31, 50, 1, 10, 16, + // Band 4 + 49, 186, 223, 17, 148, 204, 1, 96, 142, 1, 53, 83, 1, 26, 44, 1, 11, 17, + // Band 5 + 13, 217, 212, 2, 136, 180, 1, 78, 124, 1, 50, 83, 1, 29, 49, 1, 14, 23, + // Inter + // Band 0 + 197, 13, 247, 82, 17, 222, 25, 17, 162, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // Band 1 + 126, 186, 247, 234, 191, 243, 176, 177, 234, 104, 158, 220, 66, 128, 186, 55, 90, 137, + // Band 2 + 111, 197, 242, 46, 158, 219, 9, 104, 171, 2, 65, 125, 1, 44, 80, 1, 17, 91, + // Band 3 + 104, 208, 245, 39, 168, 224, 3, 109, 162, 1, 79, 124, 1, 50, 102, 1, 43, 102, + // Band 4 + 84, 220, 246, 31, 177, 231, 2, 115, 180, 1, 79, 134, 1, 55, 77, 1, 60, 79, + // Band 5 + 43, 243, 240, 8, 180, 217, 1, 115, 166, 1, 84, 121, 1, 51, 67, 1, 16, 6 + ]; + + public static byte[] GetBandTranslate(int txSize) + { + return txSize == (int)TxSize.Tx4X4 ? _coefbandTrans4X4 : _coefbandTrans8X8Plus; + } + + public static void CopyProbs(ref T dest, ReadOnlySpan probs) where T : unmanaged + { + if (Unsafe.SizeOf() != probs.Length) + { + throw new Exception("size mismatch expected: " + probs.Length + " got: " + Unsafe.SizeOf()); + } + + probs.CopyTo(MemoryMarshal.Cast(MemoryMarshal.CreateSpan(ref dest, 1))); + } + + internal const int CoefCountSat = 24; + internal const int CoefMaxUpdateFactor = 112; + internal const int CoefCountSatKey = 24; + internal const int CoefMaxUpdateFactorKey = 112; + internal const int CoefCountSatAfterKey = 24; + internal const int CoefMaxUpdateFactorAfterKey = 128; + } +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/EntropyMode.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/EntropyMode.cs new file mode 100644 index 000000000..586045976 --- /dev/null +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/EntropyMode.cs @@ -0,0 +1,390 @@ +using Ryujinx.Common.Memory; +using Ryujinx.Graphics.Nvdec.Vp9.Common; +using Ryujinx.Graphics.Nvdec.Vp9.Types; +using Ryujinx.Graphics.Video; +using System; + +namespace Ryujinx.Graphics.Nvdec.Vp9 +{ + internal class EntropyMode + { + public const int BlockSizeGroups = 4; + + public const int TxSizeContexts = 2; + + public static readonly byte[][][] KfYModeProb = + [ + [ + // above = dc + [137, 30, 42, 148, 151, 207, 70, 52, 91], // left = dc + [92, 45, 102, 136, 116, 180, 74, 90, 100], // left = v + [73, 32, 19, 187, 222, 215, 46, 34, 100], // left = h + [91, 30, 32, 116, 121, 186, 93, 86, 94], // left = d45 + [72, 35, 36, 149, 68, 206, 68, 63, 105], // left = d135 + [73, 31, 28, 138, 57, 124, 55, 122, 151], // left = d117 + [67, 23, 21, 140, 126, 197, 40, 37, 171], // left = d153 + [86, 27, 28, 128, 154, 212, 45, 43, 53], // left = d207 + [74, 32, 27, 107, 86, 160, 63, 134, 102], // left = d63 + [59, 67, 44, 140, 161, 202, 78, 67, 119] // left = tm + ], + [ + // above = v + [63, 36, 126, 146, 123, 158, 60, 90, 96], // left = dc + [43, 46, 168, 134, 107, 128, 69, 142, 92], // left = v + [44, 29, 68, 159, 201, 177, 50, 57, 77], // left = h + [58, 38, 76, 114, 97, 172, 78, 133, 92], // left = d45 + [46, 41, 76, 140, 63, 184, 69, 112, 57], // left = d135 + [38, 32, 85, 140, 46, 112, 54, 151, 133], // left = d117 + [39, 27, 61, 131, 110, 175, 44, 75, 136], // left = d153 + [52, 30, 74, 113, 130, 175, 51, 64, 58], // left = d207 + [47, 35, 80, 100, 74, 143, 64, 163, 74], // left = d63 + [36, 61, 116, 114, 128, 162, 80, 125, 82] // left = tm + ], + [ + // above = h + [82, 26, 26, 171, 208, 204, 44, 32, 105], // left = dc + [55, 44, 68, 166, 179, 192, 57, 57, 108], // left = v + [42, 26, 11, 199, 241, 228, 23, 15, 85], // left = h + [68, 42, 19, 131, 160, 199, 55, 52, 83], // left = d45 + [58, 50, 25, 139, 115, 232, 39, 52, 118], // left = d135 + [50, 35, 33, 153, 104, 162, 64, 59, 131], // left = d117 + [44, 24, 16, 150, 177, 202, 33, 19, 156], // left = d153 + [55, 27, 12, 153, 203, 218, 26, 27, 49], // left = d207 + [53, 49, 21, 110, 116, 168, 59, 80, 76], // left = d63 + [38, 72, 19, 168, 203, 212, 50, 50, 107] // left = tm + ], + [ + // above = d45 + [103, 26, 36, 129, 132, 201, 83, 80, 93], // left = dc + [59, 38, 83, 112, 103, 162, 98, 136, 90], // left = v + [62, 30, 23, 158, 200, 207, 59, 57, 50], // left = h + [67, 30, 29, 84, 86, 191, 102, 91, 59], // left = d45 + [60, 32, 33, 112, 71, 220, 64, 89, 104], // left = d135 + [53, 26, 34, 130, 56, 149, 84, 120, 103], // left = d117 + [53, 21, 23, 133, 109, 210, 56, 77, 172], // left = d153 + [77, 19, 29, 112, 142, 228, 55, 66, 36], // left = d207 + [61, 29, 29, 93, 97, 165, 83, 175, 162], // left = d63 + [47, 47, 43, 114, 137, 181, 100, 99, 95] // left = tm + ], + [ + // above = d135 + [69, 23, 29, 128, 83, 199, 46, 44, 101], // left = dc + [53, 40, 55, 139, 69, 183, 61, 80, 110], // left = v + [40, 29, 19, 161, 180, 207, 43, 24, 91], // left = h + [60, 34, 19, 105, 61, 198, 53, 64, 89], // left = d45 + [52, 31, 22, 158, 40, 209, 58, 62, 89], // left = d135 + [44, 31, 29, 147, 46, 158, 56, 102, 198], // left = d117 + [35, 19, 12, 135, 87, 209, 41, 45, 167], // left = d153 + [55, 25, 21, 118, 95, 215, 38, 39, 66], // left = d207 + [51, 38, 25, 113, 58, 164, 70, 93, 97], // left = d63 + [47, 54, 34, 146, 108, 203, 72, 103, 151] // left = tm + ], + [ + // above = d117 + [64, 19, 37, 156, 66, 138, 49, 95, 133], // left = dc + [46, 27, 80, 150, 55, 124, 55, 121, 135], // left = v + [36, 23, 27, 165, 149, 166, 54, 64, 118], // left = h + [53, 21, 36, 131, 63, 163, 60, 109, 81], // left = d45 + [40, 26, 35, 154, 40, 185, 51, 97, 123], // left = d135 + [35, 19, 34, 179, 19, 97, 48, 129, 124], // left = d117 + [36, 20, 26, 136, 62, 164, 33, 77, 154], // left = d153 + [45, 18, 32, 130, 90, 157, 40, 79, 91], // left = d207 + [45, 26, 28, 129, 45, 129, 49, 147, 123], // left = d63 + [38, 44, 51, 136, 74, 162, 57, 97, 121] // left = tm + ], + [ + // above = d153 + [75, 17, 22, 136, 138, 185, 32, 34, 166], // left = dc + [56, 39, 58, 133, 117, 173, 48, 53, 187], // left = v + [35, 21, 12, 161, 212, 207, 20, 23, 145], // left = h + [56, 29, 19, 117, 109, 181, 55, 68, 112], // left = d45 + [47, 29, 17, 153, 64, 220, 59, 51, 114], // left = d135 + [46, 16, 24, 136, 76, 147, 41, 64, 172], // left = d117 + [34, 17, 11, 108, 152, 187, 13, 15, 209], // left = d153 + [51, 24, 14, 115, 133, 209, 32, 26, 104], // left = d207 + [55, 30, 18, 122, 79, 179, 44, 88, 116], // left = d63 + [37, 49, 25, 129, 168, 164, 41, 54, 148] // left = tm + ], + [ + // above = d207 + [82, 22, 32, 127, 143, 213, 39, 41, 70], // left = dc + [62, 44, 61, 123, 105, 189, 48, 57, 64], // left = v + [47, 25, 17, 175, 222, 220, 24, 30, 86], // left = h + [68, 36, 17, 106, 102, 206, 59, 74, 74], // left = d45 + [57, 39, 23, 151, 68, 216, 55, 63, 58], // left = d135 + [49, 30, 35, 141, 70, 168, 82, 40, 115], // left = d117 + [51, 25, 15, 136, 129, 202, 38, 35, 139], // left = d153 + [68, 26, 16, 111, 141, 215, 29, 28, 28], // left = d207 + [59, 39, 19, 114, 75, 180, 77, 104, 42], // left = d63 + [40, 61, 26, 126, 152, 206, 61, 59, 93] // left = tm + ], + [ + // above = d63 + [78, 23, 39, 111, 117, 170, 74, 124, 94], // left = dc + [48, 34, 86, 101, 92, 146, 78, 179, 134], // left = v + [47, 22, 24, 138, 187, 178, 68, 69, 59], // left = h + [56, 25, 33, 105, 112, 187, 95, 177, 129], // left = d45 + [48, 31, 27, 114, 63, 183, 82, 116, 56], // left = d135 + [43, 28, 37, 121, 63, 123, 61, 192, 169], // left = d117 + [42, 17, 24, 109, 97, 177, 56, 76, 122], // left = d153 + [58, 18, 28, 105, 139, 182, 70, 92, 63], // left = d207 + [46, 23, 32, 74, 86, 150, 67, 183, 88], // left = d63 + [36, 38, 48, 92, 122, 165, 88, 137, 91] // left = tm + ], + [ + // above = tm + [65, 70, 60, 155, 159, 199, 61, 60, 81], // left = dc + [44, 78, 115, 132, 119, 173, 71, 112, 93], // left = v + [39, 38, 21, 184, 227, 206, 42, 32, 64], // left = h + [58, 47, 36, 124, 137, 193, 80, 82, 78], // left = d45 + [49, 50, 35, 144, 95, 205, 63, 78, 59], // left = d135 + [41, 53, 52, 148, 71, 142, 65, 128, 51], // left = d117 + [40, 36, 28, 143, 143, 202, 40, 55, 137], // left = d153 + [52, 34, 29, 129, 183, 227, 42, 35, 43], // left = d207 + [42, 44, 44, 104, 105, 164, 64, 130, 80], // left = d63 + [43, 81, 53, 140, 169, 204, 68, 84, 72] // left = tm + ] + ]; + + public static readonly byte[][] KfUvModeProb = + [ + [144, 11, 54, 157, 195, 130, 46, 58, 108], // y = dc + [118, 15, 123, 148, 131, 101, 44, 93, 131], // y = v + [113, 12, 23, 188, 226, 142, 26, 32, 125], // y = h + [120, 11, 50, 123, 163, 135, 64, 77, 103], // y = d45 + [113, 9, 36, 155, 111, 157, 32, 44, 161], // y = d135 + [116, 9, 55, 176, 76, 96, 37, 61, 149], // y = d117 + [115, 9, 28, 141, 161, 167, 21, 25, 193], // y = d153 + [120, 12, 32, 145, 195, 142, 32, 38, 86], // y = d207 + [116, 12, 64, 120, 140, 125, 49, 115, 121], // y = d63 + [102, 19, 66, 162, 182, 122, 35, 59, 128] // y = tm + ]; + + private static readonly byte[] _defaultIfYProbs = + [ + 65, 32, 18, 144, 162, 194, 41, 51, 98, // block_size < 8x8 + 132, 68, 18, 165, 217, 196, 45, 40, 78, // block_size < 16x16 + 173, 80, 19, 176, 240, 193, 64, 35, 46, // block_size < 32x32 + 221, 135, 38, 194, 248, 121, 96, 85, 29 // block_size >= 32x32 + ]; + + private static readonly byte[] _defaultIfUvProbs = + [ + 120, 7, 76, 176, 208, 126, 28, 54, 103, // y = dc + 48, 12, 154, 155, 139, 90, 34, 117, 119, // y = v + 67, 6, 25, 204, 243, 158, 13, 21, 96, // y = h + 97, 5, 44, 131, 176, 139, 48, 68, 97, // y = d45 + 83, 5, 42, 156, 111, 152, 26, 49, 152, // y = d135 + 80, 5, 58, 178, 74, 83, 33, 62, 145, // y = d117 + 86, 5, 32, 154, 192, 168, 14, 22, 163, // y = d153 + 85, 5, 32, 156, 216, 148, 19, 29, 73, // y = d207 + 77, 7, 64, 116, 132, 122, 37, 126, 120, // y = d63 + 101, 21, 107, 181, 192, 103, 19, 67, 125 // y = tm + ]; + + private static readonly byte[] _defaultPartitionProbs = + [ + // 8x8 . 4x4 + 199, 122, 141, // a/l both not split + 147, 63, 159, // a split, l not split + 148, 133, 118, // l split, a not split + 121, 104, 114, // a/l both split + // 16x16 . 8x8 + 174, 73, 87, // a/l both not split + 92, 41, 83, // a split, l not split + 82, 99, 50, // l split, a not split + 53, 39, 39, // a/l both split + // 32x32 . 16x16 + 177, 58, 59, // a/l both not split + 68, 26, 63, // a split, l not split + 52, 79, 25, // l split, a not split + 17, 14, 12, // a/l both split + // 64x64 . 32x32 + 222, 34, 30, // a/l both not split + 72, 16, 44, // a split, l not split + 58, 32, 12, // l split, a not split + 10, 7, 6 // a/l both split + ]; + + private static readonly byte[] _defaultInterModeProbs = + [ + 2, 173, 34, // 0 = both zero mv + 7, 145, 85, // 1 = one zero mv + one a predicted mv + 7, 166, 63, // 2 = two predicted mvs + 7, 94, 66, // 3 = one predicted/zero and one new mv + 8, 64, 46, // 4 = two new mvs + 17, 81, 31, // 5 = one intra neighbour + x + 25, 29, 30 // 6 = two intra neighbours + ]; + + /* Array indices are identical to previously-existing INTRAMODECONTEXTNODES. */ + public static readonly sbyte[] IntraModeTree = + [ + -(int)PredictionMode.DcPred, 2, /* 0 = DC_NODE */ -(int)PredictionMode.TmPred, 4, /* 1 = TM_NODE */ + -(int)PredictionMode.VPred, 6, /* 2 = V_NODE */ 8, 12, /* 3 = COM_NODE */ -(int)PredictionMode.HPred, + 10, /* 4 = H_NODE */ -(int)PredictionMode.D135Pred, -(int)PredictionMode.D117Pred, /* 5 = D135_NODE */ + -(int)PredictionMode.D45Pred, 14, /* 6 = D45_NODE */ -(int)PredictionMode.D63Pred, + 16, /* 7 = D63_NODE */ -(int)PredictionMode.D153Pred, -(int)PredictionMode.D207Pred /* 8 = D153_NODE */ + ]; + + public static readonly sbyte[] InterModeTree = + [ + -((int)PredictionMode.ZeroMv - (int)PredictionMode.NearestMv), 2, + -((int)PredictionMode.NearestMv - (int)PredictionMode.NearestMv), 4, + -((int)PredictionMode.NearMv - (int)PredictionMode.NearestMv), + -((int)PredictionMode.NewMv - (int)PredictionMode.NearestMv) + ]; + + public static readonly sbyte[] PartitionTree = + [ + -(sbyte)PartitionType.PartitionNone, 2, -(sbyte)PartitionType.PartitionHorz, 4, + -(sbyte)PartitionType.PartitionVert, -(sbyte)PartitionType.PartitionSplit + ]; + + public static readonly sbyte[] SwitchableInterpTree = + [ + -Constants.EightTap, 2, -Constants.EightTapSmooth, -Constants.EightTapSharp + ]; + + private static readonly byte[] _defaultIntraInterP = [9, 102, 187, 225]; + private static readonly byte[] _defaultCompInterP = [239, 183, 119, 96, 41]; + private static readonly byte[] _defaultCompRefP = [50, 126, 123, 221, 226]; + private static readonly byte[] _defaultSingleRefP = [33, 16, 77, 74, 142, 142, 172, 170, 238, 247]; + private static readonly byte[] _defaultTxProbs = [3, 136, 37, 5, 52, 13, 20, 152, 15, 101, 100, 66]; + + static EntropyMode() + { + byte[][] kfPartitionProbs = + [ + // 8x8 . 4x4 + [158, 97, 94], // a/l both not split + [93, 24, 99], // a split, l not split + [85, 119, 44], // l split, a not split + [62, 59, 67], // a/l both split + + // 16x16 . 8x8 + [149, 53, 53], // a/l both not split + [94, 20, 48], // a split, l not split + [83, 53, 24], // l split, a not split + [52, 18, 18], // a/l both split + + // 32x32 . 16x16 + [150, 40, 39], // a/l both not split + [78, 12, 26], // a split, l not split + [67, 33, 11], // l split, a not split + [24, 7, 5], // a/l both split + + // 64x64 . 32x32 + [174, 35, 49], // a/l both not split + [68, 11, 27], // a split, l not split + [57, 15, 9], // l split, a not split + [12, 3, 3] // a/l both split + ]; + } + + private static readonly byte[] _defaultSkipProbs = [192, 128, 64]; + + private static readonly byte[] _defaultSwitchableInterpProb = [235, 162, 36, 255, 34, 3, 149, 144]; + + private static void InitModeProbs(ref Vp9EntropyProbs fc) + { + Entropy.CopyProbs(ref fc.UvModeProb, _defaultIfUvProbs); + Entropy.CopyProbs(ref fc.YModeProb, _defaultIfYProbs); + Entropy.CopyProbs(ref fc.SwitchableInterpProb, _defaultSwitchableInterpProb); + Entropy.CopyProbs(ref fc.PartitionProb, _defaultPartitionProbs); + Entropy.CopyProbs(ref fc.IntraInterProb, _defaultIntraInterP); + Entropy.CopyProbs(ref fc.CompInterProb, _defaultCompInterP); + Entropy.CopyProbs(ref fc.CompRefProb, _defaultCompRefP); + Entropy.CopyProbs(ref fc.SingleRefProb, _defaultSingleRefP); + Entropy.CopyProbs(ref fc.Tx32x32Prob, _defaultTxProbs.AsSpan().Slice(0, 6)); + Entropy.CopyProbs(ref fc.Tx16x16Prob, _defaultTxProbs.AsSpan().Slice(6, 4)); + Entropy.CopyProbs(ref fc.Tx8x8Prob, _defaultTxProbs.AsSpan().Slice(10, 2)); + Entropy.CopyProbs(ref fc.SkipProb, _defaultSkipProbs); + Entropy.CopyProbs(ref fc.InterModeProb, _defaultInterModeProbs); + } + + internal static void TxCountsToBranchCounts32X32(ReadOnlySpan txCount32X32P, + ref Array3> ct32X32P) + { + ct32X32P[0][0] = txCount32X32P[(int)TxSize.Tx4X4]; + ct32X32P[0][1] = txCount32X32P[(int)TxSize.Tx8X8] + txCount32X32P[(int)TxSize.Tx16X16] + + txCount32X32P[(int)TxSize.Tx32X32]; + ct32X32P[1][0] = txCount32X32P[(int)TxSize.Tx8X8]; + ct32X32P[1][1] = txCount32X32P[(int)TxSize.Tx16X16] + txCount32X32P[(int)TxSize.Tx32X32]; + ct32X32P[2][0] = txCount32X32P[(int)TxSize.Tx16X16]; + ct32X32P[2][1] = txCount32X32P[(int)TxSize.Tx32X32]; + } + + internal static void TxCountsToBranchCounts16X16(ReadOnlySpan txCount16X16P, + ref Array2> ct16X16P) + { + ct16X16P[0][0] = txCount16X16P[(int)TxSize.Tx4X4]; + ct16X16P[0][1] = txCount16X16P[(int)TxSize.Tx8X8] + txCount16X16P[(int)TxSize.Tx16X16]; + ct16X16P[1][0] = txCount16X16P[(int)TxSize.Tx8X8]; + ct16X16P[1][1] = txCount16X16P[(int)TxSize.Tx16X16]; + } + + internal static void TxCountsToBranchCounts8X8(ReadOnlySpan txCount8X8P, + ref Array1> ct8X8P) + { + ct8X8P[0][0] = txCount8X8P[(int)TxSize.Tx4X4]; + ct8X8P[0][1] = txCount8X8P[(int)TxSize.Tx8X8]; + } + + public static unsafe void SetupPastIndependence(ref Vp9Common cm) + { + // Reset the segment feature data to the default stats: + // Features disabled, 0, with delta coding (Default state). + ref Types.LoopFilter lf = ref cm.Lf; + + cm.Seg.ClearAllSegFeatures(); + cm.Seg.AbsDelta = Segmentation.SegmentDeltadata; + + if (!cm.LastFrameSegMap.IsNull) + { + MemoryUtil.Fill(cm.LastFrameSegMap.ToPointer(), (byte)0, cm.MiRows * cm.MiCols); + } + + if (!cm.CurrentFrameSegMap.IsNull) + { + MemoryUtil.Fill(cm.CurrentFrameSegMap.ToPointer(), (byte)0, cm.MiRows * cm.MiCols); + } + + // Reset the mode ref deltas for loop filter + lf.LastRefDeltas = new Array4(); + lf.LastModeDeltas = new Array2(); + lf.SetDefaultLfDeltas(); + + // To force update of the sharpness + lf.LastSharpnessLevel = -1; + + cm.DefaultCoefProbs(); + InitModeProbs(ref cm.Fc.Value); + cm.InitMvProbs(); + + if (cm.FrameType == FrameType.KeyFrame || cm.ErrorResilientMode != 0 || cm.ResetFrameContext == 3) + { + // Reset all frame contexts. + for (int i = 0; i < Constants.FrameContexts; ++i) + { + cm.FrameContexts[i] = cm.Fc.Value; + } + } + else if (cm.ResetFrameContext == 2) + { + // Reset only the frame context specified in the frame header. + cm.FrameContexts[(int)cm.FrameContextIdx] = cm.Fc.Value; + } + + // prev_mip will only be allocated in encoder. + if (cm.FrameIsIntraOnly() && !cm.PrevMip.IsNull) + { + cm.PrevMi.Value = new ModeInfo(); + } + + cm.RefFrameSignBias = new Array4(); + + cm.FrameContextIdx = 0; + } + } +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/EntropyMv.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/EntropyMv.cs new file mode 100644 index 000000000..4fa8e6d4c --- /dev/null +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/EntropyMv.cs @@ -0,0 +1,165 @@ +using Ryujinx.Common.Memory; +using Ryujinx.Graphics.Nvdec.Vp9.Types; +using Ryujinx.Graphics.Video; +using System.Diagnostics; + +namespace Ryujinx.Graphics.Nvdec.Vp9 +{ + internal static class EntropyMv + { + public const int UpdateProb = 252; + + /* Symbols for coding which components are zero jointly */ + public const int Joints = 4; + + + public static readonly sbyte[] JointTree = + [ + -(sbyte)MvJointType.Zero, 2, -(sbyte)MvJointType.Hnzvz, 4, + -(sbyte)MvJointType.Hzvnz, -(sbyte)MvJointType.Hnzvnz + ]; + + public static readonly sbyte[] ClassTree = + [ + -(sbyte)MvClassType.Class0, 2, -(sbyte)MvClassType.Class1, 4, 6, 8, -(sbyte)MvClassType.Class2, + -(sbyte)MvClassType.Class3, 10, 12, -(sbyte)MvClassType.Class4, -(sbyte)MvClassType.Class5, + -(sbyte)MvClassType.Class6, 14, 16, 18, -(sbyte)MvClassType.Class7, -(sbyte)MvClassType.Class8, + -(sbyte)MvClassType.Class9, -(sbyte)MvClassType.Class10 + ]; + + public static readonly sbyte[] Class0Tree = [-0, -1]; + + public static readonly sbyte[] FpTree = [-0, 2, -1, 4, -2, -3]; + + private static bool JointVertical(MvJointType type) + { + return type == MvJointType.Hzvnz || type == MvJointType.Hnzvnz; + } + + private static bool JointHorizontal(MvJointType type) + { + return type == MvJointType.Hnzvz || type == MvJointType.Hnzvnz; + } + + private static readonly byte[] _logInBase2 = + [ + 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 10 + ]; + + private static int ClassBase(MvClassType c) + { + return c != 0 ? Class0Size << ((int)c + 2) : 0; + } + + private static MvClassType GetClass(int z, Ptr offset) + { + MvClassType c = z >= Class0Size * 4096 + ? MvClassType.Class10 + : (MvClassType)_logInBase2[z >> 3]; + if (!offset.IsNull) + { + offset.Value = z - ClassBase(c); + } + + return c; + } + + private static void IncComponent(int v, ref Vp9BackwardUpdates compCounts, int compIndex, int incr, int usehp) + { + int o = 0; + Debug.Assert(v != 0); /* should not be zero */ + int s = v < 0 ? 1 : 0; + compCounts.Sign[compIndex][s] += (uint)incr; + int z = (s != 0 ? -v : v) - 1 /* magnitude - 1 */; + + int c = (int)GetClass(z, new Ptr(ref o)); + compCounts.Classes[compIndex][c] += (uint)incr; + + int d = o >> 3 /* int mv data */; + int f = (o >> 1) & 3 /* fractional pel mv data */; + int e = o & 1 /* high precision mv data */; + + if (c == (int)MvClassType.Class0) + { + compCounts.Class0[compIndex][d] += (uint)incr; + compCounts.Class0Fp[compIndex][d][f] += (uint)incr; + compCounts.Class0Hp[compIndex][e] += (uint)(usehp * incr); + } + else + { + int b = c + Class0Bits - 1; // number of bits + for (int i = 0; i < b; ++i) + { + compCounts.Bits[compIndex][i][(d >> i) & 1] += (uint)incr; + } + + compCounts.Fp[compIndex][f] += (uint)incr; + compCounts.Hp[compIndex][e] += (uint)(usehp * incr); + } + } + + public static void Inc(ref Mv mv, Ptr counts) + { + if (!counts.IsNull) + { + MvJointType j = mv.GetJoint(); + ++counts.Value.Joints[(int)j]; + + if (JointVertical(j)) + { + IncComponent(mv.Row, ref counts.Value, 0, 1, 1); + } + + if (JointHorizontal(j)) + { + IncComponent(mv.Col, ref counts.Value, 1, 1, 1); + } + } + } + + /* Symbols for coding magnitude class of nonzero components */ + public const int Classes = 11; + + public const int Class0Bits = 1; /* bits at integer precision for class 0 */ + public const int Class0Size = 1 << Class0Bits; + public const int OffsetBits = Classes + Class0Bits - 2; + public const int FpSize = 4; + + public const int MaxBits = Classes + Class0Bits + 2; + public const int Max = (1 << MaxBits) - 1; + public const int Vals = (Max << 1) + 1; + + public const int InUseBits = 14; + public const int Upp = (1 << InUseBits) - 1; + public const int Low = -(1 << InUseBits); + } +} diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/FrameBuffers.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/FrameBuffers.cs new file mode 100644 index 000000000..a5692c446 --- /dev/null +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/FrameBuffers.cs @@ -0,0 +1,78 @@ +using Ryujinx.Common.Memory; +using Ryujinx.Graphics.Nvdec.Vp9.Common; +using Ryujinx.Graphics.Nvdec.Vp9.Types; + +namespace Ryujinx.Graphics.Nvdec.Vp9 +{ + internal struct InternalFrameBuffer + { + public ArrayPtr Data; + public bool InUse; + } + + internal struct InternalFrameBufferList + { + public ArrayPtr IntFb; + } + + internal static class FrameBuffers + { + public static int GetFrameBuffer(MemoryAllocator allocator, Ptr cbPriv, ulong minSize, + ref VpxCodecFrameBuffer fb) + { + int i; + if (cbPriv.IsNull) + { + return -1; + } + + // Find a free frame buffer. + for (i = 0; i < cbPriv.Value.IntFb.Length; ++i) + { + if (!cbPriv.Value.IntFb[i].InUse) + { + break; + } + } + + if (i == cbPriv.Value.IntFb.Length) + { + return -1; + } + + if ((ulong)cbPriv.Value.IntFb[i].Data.Length < minSize) + { + if (!cbPriv.Value.IntFb[i].Data.IsNull) + { + allocator.Free(cbPriv.Value.IntFb[i].Data); + } + + // The data must be zeroed to fix a valgrind error from the C loop filter + // due to access uninitialized memory in frame border. It could be + // skipped if border were totally removed. + cbPriv.Value.IntFb[i].Data = allocator.Allocate((int)minSize); + if (cbPriv.Value.IntFb[i].Data.IsNull) + { + return -1; + } + } + + fb.Data = cbPriv.Value.IntFb[i].Data; + cbPriv.Value.IntFb[i].InUse = true; + + // Set the frame buffer's private data to point at the internal frame buffer. + fb.Priv = new Ptr(ref cbPriv.Value.IntFb[i]); + return 0; + } + + public static int ReleaseFrameBuffer(Ptr cbPriv, ref VpxCodecFrameBuffer fb) + { + if (!fb.Priv.IsNull) + { + fb.Priv.Value.InUse = false; + } + + return 0; + } + } +} diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Idct.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Idct.cs index ebebf0ef9..f080fc825 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Idct.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Idct.cs @@ -1,4 +1,4 @@ -using Ryujinx.Graphics.Nvdec.Vp9.Common; +using Ryujinx.Graphics.Nvdec.Vp9.Common; using Ryujinx.Graphics.Nvdec.Vp9.Types; using System; using static Ryujinx.Graphics.Nvdec.Vp9.Dsp.InvTxfm; @@ -8,11 +8,13 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 internal static class Idct { private delegate void Transform1D(ReadOnlySpan input, Span output); + private delegate void HighbdTransform1D(ReadOnlySpan input, Span output, int bd); private struct Transform2D { - public Transform1D Cols, Rows; // Vertical and horizontal + public readonly Transform1D Cols; // Vertical and horizontal + public readonly Transform1D Rows; // Vertical and horizontal public Transform2D(Transform1D cols, Transform1D rows) { @@ -23,7 +25,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 private struct HighbdTransform2D { - public HighbdTransform1D Cols, Rows; // Vertical and horizontal + public readonly HighbdTransform1D Cols; // Vertical and horizontal + public readonly HighbdTransform1D Rows; // Vertical and horizontal public HighbdTransform2D(HighbdTransform1D cols, HighbdTransform1D rows) { @@ -32,55 +35,56 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 } } - private static readonly Transform2D[] _iht4 = { + private static readonly Transform2D[] _iht4 = + [ new(Idct4, Idct4), // DCT_DCT = 0 new(Iadst4, Idct4), // ADST_DCT = 1 new(Idct4, Iadst4), // DCT_ADST = 2 - new(Iadst4, Iadst4), // ADST_ADST = 3 - }; + new(Iadst4, Iadst4) // ADST_ADST = 3 + ]; - public static void Iht4x416Add(ReadOnlySpan input, Span dest, int stride, int txType) + public static void Iht4X416Add(ReadOnlySpan input, Span dest, int stride, int txType) { - int i, j; Span output = stackalloc int[4 * 4]; Span outptr = output; Span tempIn = stackalloc int[4]; Span tempOut = stackalloc int[4]; // Inverse transform row vectors - for (i = 0; i < 4; ++i) + for (int i = 0; i < 4; ++i) { _iht4[txType].Rows(input, outptr); - input = input[4..]; - outptr = outptr[4..]; + input = input.Slice(4); + outptr = outptr.Slice(4); } // Inverse transform column vectors - for (i = 0; i < 4; ++i) + for (int i = 0; i < 4; ++i) { - for (j = 0; j < 4; ++j) + for (int j = 0; j < 4; ++j) { - tempIn[j] = output[j * 4 + i]; + tempIn[j] = output[(j * 4) + i]; } _iht4[txType].Cols(tempIn, tempOut); - for (j = 0; j < 4; ++j) + for (int j = 0; j < 4; ++j) { - dest[j * stride + i] = ClipPixelAdd(dest[j * stride + i], BitUtils.RoundPowerOfTwo(tempOut[j], 4)); + dest[(j * stride) + i] = + ClipPixelAdd(dest[(j * stride) + i], BitUtils.RoundPowerOfTwo(tempOut[j], 4)); } } } - private static readonly Transform2D[] _iht8 = { + private static readonly Transform2D[] _iht8 = + [ new(Idct8, Idct8), // DCT_DCT = 0 new(Iadst8, Idct8), // ADST_DCT = 1 new(Idct8, Iadst8), // DCT_ADST = 2 - new(Iadst8, Iadst8), // ADST_ADST = 3 - }; + new(Iadst8, Iadst8) // ADST_ADST = 3 + ]; - public static void Iht8x864Add(ReadOnlySpan input, Span dest, int stride, int txType) + public static void Iht8X864Add(ReadOnlySpan input, Span dest, int stride, int txType) { - int i, j; Span output = stackalloc int[8 * 8]; Span outptr = output; Span tempIn = stackalloc int[8]; @@ -88,39 +92,40 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 Transform2D ht = _iht8[txType]; // Inverse transform row vectors - for (i = 0; i < 8; ++i) + for (int i = 0; i < 8; ++i) { ht.Rows(input, outptr); - input = input[8..]; - outptr = outptr[8..]; + input = input.Slice(8); + outptr = outptr.Slice(8); } // Inverse transform column vectors - for (i = 0; i < 8; ++i) + for (int i = 0; i < 8; ++i) { - for (j = 0; j < 8; ++j) + for (int j = 0; j < 8; ++j) { - tempIn[j] = output[j * 8 + i]; + tempIn[j] = output[(j * 8) + i]; } ht.Cols(tempIn, tempOut); - for (j = 0; j < 8; ++j) + for (int j = 0; j < 8; ++j) { - dest[j * stride + i] = ClipPixelAdd(dest[j * stride + i], BitUtils.RoundPowerOfTwo(tempOut[j], 5)); + dest[(j * stride) + i] = + ClipPixelAdd(dest[(j * stride) + i], BitUtils.RoundPowerOfTwo(tempOut[j], 5)); } } } - private static readonly Transform2D[] _iht16 = { + private static readonly Transform2D[] _iht16 = + [ new(Idct16, Idct16), // DCT_DCT = 0 new(Iadst16, Idct16), // ADST_DCT = 1 new(Idct16, Iadst16), // DCT_ADST = 2 - new(Iadst16, Iadst16), // ADST_ADST = 3 - }; + new(Iadst16, Iadst16) // ADST_ADST = 3 + ]; - public static void Iht16x16256Add(ReadOnlySpan input, Span dest, int stride, int txType) + public static void Iht16X16256Add(ReadOnlySpan input, Span dest, int stride, int txType) { - int i, j; Span output = stackalloc int[16 * 16]; Span outptr = output; Span tempIn = stackalloc int[16]; @@ -128,55 +133,56 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 Transform2D ht = _iht16[txType]; // Rows - for (i = 0; i < 16; ++i) + for (int i = 0; i < 16; ++i) { ht.Rows(input, outptr); - input = input[16..]; - outptr = outptr[16..]; + input = input.Slice(16); + outptr = outptr.Slice(16); } // Columns - for (i = 0; i < 16; ++i) + for (int i = 0; i < 16; ++i) { - for (j = 0; j < 16; ++j) + for (int j = 0; j < 16; ++j) { - tempIn[j] = output[j * 16 + i]; + tempIn[j] = output[(j * 16) + i]; } ht.Cols(tempIn, tempOut); - for (j = 0; j < 16; ++j) + for (int j = 0; j < 16; ++j) { - dest[j * stride + i] = ClipPixelAdd(dest[j * stride + i], BitUtils.RoundPowerOfTwo(tempOut[j], 6)); + dest[(j * stride) + i] = + ClipPixelAdd(dest[(j * stride) + i], BitUtils.RoundPowerOfTwo(tempOut[j], 6)); } } } // Idct - public static void Idct4x4Add(ReadOnlySpan input, Span dest, int stride, int eob) + public static void Idct4X4Add(ReadOnlySpan input, Span dest, int stride, int eob) { if (eob > 1) { - Idct4x416Add(input, dest, stride); + Idct4X416Add(input, dest, stride); } else { - Idct4x41Add(input, dest, stride); + Idct4X41Add(input, dest, stride); } } - public static void Iwht4x4Add(ReadOnlySpan input, Span dest, int stride, int eob) + public static void Iwht4X4Add(ReadOnlySpan input, Span dest, int stride, int eob) { if (eob > 1) { - Iwht4x416Add(input, dest, stride); + Iwht4X416Add(input, dest, stride); } else { - Iwht4x41Add(input, dest, stride); + Iwht4X41Add(input, dest, stride); } } - public static void Idct8x8Add(ReadOnlySpan input, Span dest, int stride, int eob) + public static void Idct8X8Add(ReadOnlySpan input, Span dest, int stride, int eob) { // If dc is 1, then input[0] is the reconstructed value, do not need // dequantization. Also, when dc is 1, dc is counted in eobs, namely eobs >=1. @@ -186,149 +192,150 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 if (eob == 1) { // DC only DCT coefficient - Idct8x81Add(input, dest, stride); + Idct8X81Add(input, dest, stride); } else if (eob <= 12) { - Idct8x812Add(input, dest, stride); + Idct8X812Add(input, dest, stride); } else { - Idct8x864Add(input, dest, stride); + Idct8X864Add(input, dest, stride); } } - public static void Idct16x16Add(ReadOnlySpan input, Span dest, int stride, int eob) + public static void Idct16X16Add(ReadOnlySpan input, Span dest, int stride, int eob) { /* The calculation can be simplified if there are not many non-zero dct * coefficients. Use eobs to separate different cases. */ if (eob == 1) /* DC only DCT coefficient. */ { - Idct16x161Add(input, dest, stride); + Idct16X161Add(input, dest, stride); } else if (eob <= 10) { - Idct16x1610Add(input, dest, stride); + Idct16X1610Add(input, dest, stride); } else if (eob <= 38) { - Idct16x1638Add(input, dest, stride); + Idct16X1638Add(input, dest, stride); } else { - Idct16x16256Add(input, dest, stride); + Idct16X16256Add(input, dest, stride); } } - public static void Idct32x32Add(ReadOnlySpan input, Span dest, int stride, int eob) + public static void Idct32X32Add(ReadOnlySpan input, Span dest, int stride, int eob) { if (eob == 1) { - Idct32x321Add(input, dest, stride); + Idct32X321Add(input, dest, stride); } else if (eob <= 34) { // Non-zero coeff only in upper-left 8x8 - Idct32x3234Add(input, dest, stride); + Idct32X3234Add(input, dest, stride); } else if (eob <= 135) { // Non-zero coeff only in upper-left 16x16 - Idct32x32135Add(input, dest, stride); + Idct32X32135Add(input, dest, stride); } else { - Idct32x321024Add(input, dest, stride); + Idct32X321024Add(input, dest, stride); } } // Iht - public static void Iht4x4Add(TxType txType, ReadOnlySpan input, Span dest, int stride, int eob) + public static void Iht4X4Add(TxType txType, ReadOnlySpan input, Span dest, int stride, int eob) { if (txType == TxType.DctDct) { - Idct4x4Add(input, dest, stride, eob); + Idct4X4Add(input, dest, stride, eob); } else { - Iht4x416Add(input, dest, stride, (int)txType); + Iht4X416Add(input, dest, stride, (int)txType); } } - public static void Iht8x8Add(TxType txType, ReadOnlySpan input, Span dest, int stride, int eob) + public static void Iht8X8Add(TxType txType, ReadOnlySpan input, Span dest, int stride, int eob) { if (txType == TxType.DctDct) { - Idct8x8Add(input, dest, stride, eob); + Idct8X8Add(input, dest, stride, eob); } else { - Iht8x864Add(input, dest, stride, (int)txType); + Iht8X864Add(input, dest, stride, (int)txType); } } - public static void Iht16x16Add(TxType txType, ReadOnlySpan input, Span dest, - int stride, int eob) + public static void Iht16X16Add(TxType txType, ReadOnlySpan input, Span dest, + int stride, int eob) { if (txType == TxType.DctDct) { - Idct16x16Add(input, dest, stride, eob); + Idct16X16Add(input, dest, stride, eob); } else { - Iht16x16256Add(input, dest, stride, (int)txType); + Iht16X16256Add(input, dest, stride, (int)txType); } } - private static readonly HighbdTransform2D[] _highbdIht4 = { + private static readonly HighbdTransform2D[] _highbdIht4 = + [ new(HighbdIdct4, HighbdIdct4), // DCT_DCT = 0 new(HighbdIadst4, HighbdIdct4), // ADST_DCT = 1 new(HighbdIdct4, HighbdIadst4), // DCT_ADST = 2 - new(HighbdIadst4, HighbdIadst4), // ADST_ADST = 3 - }; + new(HighbdIadst4, HighbdIadst4) // ADST_ADST = 3 + ]; - public static void HighbdIht4x416Add(ReadOnlySpan input, Span dest, int stride, int txType, int bd) + public static void HighbdIht4X416Add(ReadOnlySpan input, Span dest, int stride, int txType, int bd) { - int i, j; Span output = stackalloc int[4 * 4]; Span outptr = output; Span tempIn = stackalloc int[4]; Span tempOut = stackalloc int[4]; // Inverse transform row vectors. - for (i = 0; i < 4; ++i) + for (int i = 0; i < 4; ++i) { _highbdIht4[txType].Rows(input, outptr, bd); - input = input[4..]; - outptr = outptr[4..]; + input = input.Slice(4); + outptr = outptr.Slice(4); } // Inverse transform column vectors. - for (i = 0; i < 4; ++i) + for (int i = 0; i < 4; ++i) { - for (j = 0; j < 4; ++j) + for (int j = 0; j < 4; ++j) { - tempIn[j] = output[j * 4 + i]; + tempIn[j] = output[(j * 4) + i]; } _highbdIht4[txType].Cols(tempIn, tempOut, bd); - for (j = 0; j < 4; ++j) + for (int j = 0; j < 4; ++j) { - dest[j * stride + i] = HighbdClipPixelAdd(dest[j * stride + i], BitUtils.RoundPowerOfTwo(tempOut[j], 4), bd); + dest[(j * stride) + i] = HighbdClipPixelAdd(dest[(j * stride) + i], + BitUtils.RoundPowerOfTwo(tempOut[j], 4), bd); } } } - private static readonly HighbdTransform2D[] _highIht8 = { + private static readonly HighbdTransform2D[] _highIht8 = + [ new(HighbdIdct8, HighbdIdct8), // DCT_DCT = 0 new(HighbdIadst8, HighbdIdct8), // ADST_DCT = 1 new(HighbdIdct8, HighbdIadst8), // DCT_ADST = 2 - new(HighbdIadst8, HighbdIadst8), // ADST_ADST = 3 - }; + new(HighbdIadst8, HighbdIadst8) // ADST_ADST = 3 + ]; - public static void HighbdIht8x864Add(ReadOnlySpan input, Span dest, int stride, int txType, int bd) + public static void HighbdIht8X864Add(ReadOnlySpan input, Span dest, int stride, int txType, int bd) { - int i, j; Span output = stackalloc int[8 * 8]; Span outptr = output; Span tempIn = stackalloc int[8]; @@ -336,39 +343,41 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 HighbdTransform2D ht = _highIht8[txType]; // Inverse transform row vectors. - for (i = 0; i < 8; ++i) + for (int i = 0; i < 8; ++i) { ht.Rows(input, outptr, bd); - input = input[8..]; - outptr = output[8..]; + input = input.Slice(8); + outptr = output.Slice(8); } // Inverse transform column vectors. - for (i = 0; i < 8; ++i) + for (int i = 0; i < 8; ++i) { - for (j = 0; j < 8; ++j) + for (int j = 0; j < 8; ++j) { - tempIn[j] = output[j * 8 + i]; + tempIn[j] = output[(j * 8) + i]; } ht.Cols(tempIn, tempOut, bd); - for (j = 0; j < 8; ++j) + for (int j = 0; j < 8; ++j) { - dest[j * stride + i] = HighbdClipPixelAdd(dest[j * stride + i], BitUtils.RoundPowerOfTwo(tempOut[j], 5), bd); + dest[(j * stride) + i] = HighbdClipPixelAdd(dest[(j * stride) + i], + BitUtils.RoundPowerOfTwo(tempOut[j], 5), bd); } } } - private static readonly HighbdTransform2D[] _highIht16 = { + private static readonly HighbdTransform2D[] _highIht16 = + [ new(HighbdIdct16, HighbdIdct16), // DCT_DCT = 0 new(HighbdIadst16, HighbdIdct16), // ADST_DCT = 1 new(HighbdIdct16, HighbdIadst16), // DCT_ADST = 2 - new(HighbdIadst16, HighbdIadst16), // ADST_ADST = 3 - }; + new(HighbdIadst16, HighbdIadst16) // ADST_ADST = 3 + ]; - public static void HighbdIht16x16256Add(ReadOnlySpan input, Span dest, int stride, int txType, int bd) + public static void HighbdIht16X16256Add(ReadOnlySpan input, Span dest, int stride, int txType, + int bd) { - int i, j; Span output = stackalloc int[16 * 16]; Span outptr = output; Span tempIn = stackalloc int[16]; @@ -376,55 +385,56 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 HighbdTransform2D ht = _highIht16[txType]; // Rows - for (i = 0; i < 16; ++i) + for (int i = 0; i < 16; ++i) { ht.Rows(input, outptr, bd); - input = input[16..]; - outptr = output[16..]; + input = input.Slice(16); + outptr = output.Slice(16); } // Columns - for (i = 0; i < 16; ++i) + for (int i = 0; i < 16; ++i) { - for (j = 0; j < 16; ++j) + for (int j = 0; j < 16; ++j) { - tempIn[j] = output[j * 16 + i]; + tempIn[j] = output[(j * 16) + i]; } ht.Cols(tempIn, tempOut, bd); - for (j = 0; j < 16; ++j) + for (int j = 0; j < 16; ++j) { - dest[j * stride + i] = HighbdClipPixelAdd(dest[j * stride + i], BitUtils.RoundPowerOfTwo(tempOut[j], 6), bd); + dest[(j * stride) + i] = HighbdClipPixelAdd(dest[(j * stride) + i], + BitUtils.RoundPowerOfTwo(tempOut[j], 6), bd); } } } // Idct - public static void HighbdIdct4x4Add(ReadOnlySpan input, Span dest, int stride, int eob, int bd) + public static void HighbdIdct4X4Add(ReadOnlySpan input, Span dest, int stride, int eob, int bd) { if (eob > 1) { - HighbdIdct4x416Add(input, dest, stride, bd); + HighbdIdct4X416Add(input, dest, stride, bd); } else { - HighbdIdct4x41Add(input, dest, stride, bd); + HighbdIdct4X41Add(input, dest, stride, bd); } } - public static void HighbdIwht4x4Add(ReadOnlySpan input, Span dest, int stride, int eob, int bd) + public static void HighbdIwht4X4Add(ReadOnlySpan input, Span dest, int stride, int eob, int bd) { if (eob > 1) { - HighbdIwht4x416Add(input, dest, stride, bd); + HighbdIwht4X416Add(input, dest, stride, bd); } else { - HighbdIwht4x41Add(input, dest, stride, bd); + HighbdIwht4X41Add(input, dest, stride, bd); } } - public static void HighbdIdct8x8Add(ReadOnlySpan input, Span dest, int stride, int eob, int bd) + public static void HighbdIdct8X8Add(ReadOnlySpan input, Span dest, int stride, int eob, int bd) { // If dc is 1, then input[0] is the reconstructed value, do not need // dequantization. Also, when dc is 1, dc is counted in eobs, namely eobs >=1. @@ -434,97 +444,100 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 // DC only DCT coefficient if (eob == 1) { - Vpx_Highbdidct8x8_1_add_c(input, dest, stride, bd); + VpxHighbdidct8X81AddC(input, dest, stride, bd); } else if (eob <= 12) { - HighbdIdct8x812Add(input, dest, stride, bd); + HighbdIdct8X812Add(input, dest, stride, bd); } else { - HighbdIdct8x864Add(input, dest, stride, bd); + HighbdIdct8X864Add(input, dest, stride, bd); } } - public static void HighbdIdct16x16Add(ReadOnlySpan input, Span dest, int stride, int eob, int bd) + public static void HighbdIdct16X16Add(ReadOnlySpan input, Span dest, int stride, int eob, int bd) { // The calculation can be simplified if there are not many non-zero dct // coefficients. Use eobs to separate different cases. // DC only DCT coefficient. if (eob == 1) { - HighbdIdct16x161Add(input, dest, stride, bd); + HighbdIdct16X161Add(input, dest, stride, bd); } else if (eob <= 10) { - HighbdIdct16x1610Add(input, dest, stride, bd); + HighbdIdct16X1610Add(input, dest, stride, bd); } else if (eob <= 38) { - HighbdIdct16x1638Add(input, dest, stride, bd); + HighbdIdct16X1638Add(input, dest, stride, bd); } else { - HighbdIdct16x16256Add(input, dest, stride, bd); + HighbdIdct16X16256Add(input, dest, stride, bd); } } - public static void HighbdIdct32x32Add(ReadOnlySpan input, Span dest, int stride, int eob, int bd) + public static void HighbdIdct32X32Add(ReadOnlySpan input, Span dest, int stride, int eob, int bd) { // Non-zero coeff only in upper-left 8x8 if (eob == 1) { - HighbdIdct32x321Add(input, dest, stride, bd); + HighbdIdct32X321Add(input, dest, stride, bd); } else if (eob <= 34) { - HighbdIdct32x3234Add(input, dest, stride, bd); + HighbdIdct32X3234Add(input, dest, stride, bd); } else if (eob <= 135) { - HighbdIdct32x32135Add(input, dest, stride, bd); + HighbdIdct32X32135Add(input, dest, stride, bd); } else { - HighbdIdct32x321024Add(input, dest, stride, bd); + HighbdIdct32X321024Add(input, dest, stride, bd); } } // Iht - public static void HighbdIht4x4Add(TxType txType, ReadOnlySpan input, Span dest, int stride, int eob, int bd) + public static void HighbdIht4X4Add(TxType txType, ReadOnlySpan input, Span dest, int stride, + int eob, int bd) { if (txType == TxType.DctDct) { - HighbdIdct4x4Add(input, dest, stride, eob, bd); + HighbdIdct4X4Add(input, dest, stride, eob, bd); } else { - HighbdIht4x416Add(input, dest, stride, (int)txType, bd); + HighbdIht4X416Add(input, dest, stride, (int)txType, bd); } } - public static void HighbdIht8x8Add(TxType txType, ReadOnlySpan input, Span dest, int stride, int eob, int bd) + public static void HighbdIht8X8Add(TxType txType, ReadOnlySpan input, Span dest, int stride, + int eob, int bd) { if (txType == TxType.DctDct) { - HighbdIdct8x8Add(input, dest, stride, eob, bd); + HighbdIdct8X8Add(input, dest, stride, eob, bd); } else { - HighbdIht8x864Add(input, dest, stride, (int)txType, bd); + HighbdIht8X864Add(input, dest, stride, (int)txType, bd); } } - public static void HighbdIht16x16Add(TxType txType, ReadOnlySpan input, Span dest, int stride, int eob, int bd) + public static void HighbdIht16X16Add(TxType txType, ReadOnlySpan input, Span dest, int stride, + int eob, int bd) { if (txType == TxType.DctDct) { - HighbdIdct16x16Add(input, dest, stride, eob, bd); + HighbdIdct16X16Add(input, dest, stride, eob, bd); } else { - HighbdIht16x16256Add(input, dest, stride, (int)txType, bd); + HighbdIht16X16256Add(input, dest, stride, (int)txType, bd); } } } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/InternalErrorException.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/InternalErrorException.cs index d4ba0d88b..efcd9c23b 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/InternalErrorException.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/InternalErrorException.cs @@ -2,7 +2,7 @@ using System; namespace Ryujinx.Graphics.Nvdec.Vp9 { - class InternalErrorException : Exception + internal class InternalErrorException : Exception { public InternalErrorException(string message) : base(message) { @@ -12,4 +12,4 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { } } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/InternalErrorInfo.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/InternalErrorInfo.cs index cfec81ed1..9acfc211d 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/InternalErrorInfo.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/InternalErrorInfo.cs @@ -11,4 +11,4 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 throw new InternalErrorException(message); } } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/LoopFilter.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/LoopFilter.cs index 9b2564d70..937a196dd 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/LoopFilter.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/LoopFilter.cs @@ -1,8 +1,12 @@ -using Ryujinx.Common.Memory; +using Ryujinx.Common.Memory; using Ryujinx.Graphics.Nvdec.Vp9.Common; +using Ryujinx.Graphics.Nvdec.Vp9.Dsp; using Ryujinx.Graphics.Nvdec.Vp9.Types; using System; +using System.Diagnostics; using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; namespace Ryujinx.Graphics.Nvdec.Vp9 { @@ -13,11 +17,119 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 public const int MaxRefLfDeltas = 4; public const int MaxModeLfDeltas = 2; + private struct LfSync + { + private int[] _curSbCol; + private object[] _syncObjects; + private int _syncRange; + + private static int GetSyncRange(int width) + { + // nsync numbers are picked by testing. For example, for 4k + // video, using 4 gives best performance. + if (width < 640) + { + return 1; + } + + if (width <= 1280) + { + return 2; + } + + if (width <= 4096) + { + return 4; + } + + return 8; + } + + public void Initialize(int width, int sbRows) + { + if (_curSbCol == null || _curSbCol.Length != sbRows) + { + _curSbCol = new int[sbRows]; + _syncObjects = new object[sbRows]; + + for (int i = 0; i < sbRows; i++) + { + _syncObjects[i] = new object(); + } + } + + _syncRange = GetSyncRange(width); + _curSbCol.AsSpan().Fill(-1); + } + + public void SyncRead(int r, int c) + { + if (_curSbCol == null) + { + return; + } + + int nsync = _syncRange; + + if (r != 0 && (c & (nsync - 1)) == 0) + { + object syncObject = _syncObjects[r - 1]; + lock (syncObject) + { + while (c > _curSbCol[r - 1] - nsync) + { + Monitor.Wait(syncObject); + } + } + } + } + + public void SyncWrite(int r, int c, int sbCols) + { + if (_curSbCol == null) + { + return; + } + + int nsync = _syncRange; + + int cur; + // Only signal when there are enough filtered SB for next row to run. + bool sig = true; + + if (c < sbCols - 1) + { + cur = c; + + if (c % nsync != 0) + { + sig = false; + } + } + else + { + cur = sbCols + nsync; + } + + if (sig) + { + object syncObject = _syncObjects[r]; + + lock (syncObject) + { + _curSbCol[r] = cur; + + Monitor.Pulse(syncObject); + } + } + } + } + // 64 bit masks for left transform size. Each 1 represents a position where // we should apply a loop filter across the left border of an 8x8 block // boundary. // - // In the case of TX_16X16 -> ( in low order byte first we end up with + // In the case of (int)TxSize.Tx16x16 . ( in low order byte first we end up with // a mask that looks like this // // 10101010 @@ -30,18 +142,19 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 // 10101010 // // A loopfilter should be applied to every other 8x8 horizontally. - private static readonly ulong[] _left64X64TxformMask = { - 0xffffffffffffffffUL, // TX_4X4 - 0xffffffffffffffffUL, // TX_8x8 - 0x5555555555555555UL, // TX_16x16 - 0x1111111111111111UL, // TX_32x32 - }; + private static readonly ulong[] _left64X64TxformMask = + [ + 0xffffffffffffffffUL, // (int)TxSize.Tx4x4 + 0xffffffffffffffffUL, // (int)TxSize.Tx8x8 + 0x5555555555555555UL, // (int)TxSize.Tx16x16 + 0x1111111111111111UL // (int)TxSize.Tx32x32 + ]; // 64 bit masks for above transform size. Each 1 represents a position where // we should apply a loop filter across the top border of an 8x8 block // boundary. // - // In the case of TX_32x32 -> ( in low order byte first we end up with + // In the case of (int)TxSize.Tx32x32 . ( in low order byte first we end up with // a mask that looks like this // // 11111111 @@ -54,18 +167,19 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 // 00000000 // // A loopfilter should be applied to every other 4 the row vertically. - private static readonly ulong[] _above64X64TxformMask = { - 0xffffffffffffffffUL, // TX_4X4 - 0xffffffffffffffffUL, // TX_8x8 - 0x00ff00ff00ff00ffUL, // TX_16x16 - 0x000000ff000000ffUL, // TX_32x32 - }; + private static readonly ulong[] _above64X64TxformMask = + [ + 0xffffffffffffffffUL, // (int)TxSize.Tx4x4 + 0xffffffffffffffffUL, // (int)TxSize.Tx8x8 + 0x00ff00ff00ff00ffUL, // (int)TxSize.Tx16x16 + 0x000000ff000000ffUL // (int)TxSize.Tx32x32 + ]; // 64 bit masks for prediction sizes (left). Each 1 represents a position // where left border of an 8x8 block. These are aligned to the right most // appropriate bit, and then shifted into place. // - // In the case of TX_16x32 -> ( low order byte first ) we end up with + // In the case of TX_16x32 . ( low order byte first ) we end up with // a mask that looks like this : // // 10000000 @@ -76,158 +190,164 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 // 00000000 // 00000000 // 00000000 - private static readonly ulong[] _leftPredictionMask = { - 0x0000000000000001UL, // BLOCK_4X4, - 0x0000000000000001UL, // BLOCK_4X8, - 0x0000000000000001UL, // BLOCK_8X4, - 0x0000000000000001UL, // BLOCK_8X8, - 0x0000000000000101UL, // BLOCK_8X16, - 0x0000000000000001UL, // BLOCK_16X8, - 0x0000000000000101UL, // BLOCK_16X16, - 0x0000000001010101UL, // BLOCK_16X32, - 0x0000000000000101UL, // BLOCK_32X16, - 0x0000000001010101UL, // BLOCK_32X32, - 0x0101010101010101UL, // BLOCK_32X64, - 0x0000000001010101UL, // BLOCK_64X32, - 0x0101010101010101UL, // BLOCK_64X64 - }; + private static readonly ulong[] _leftPredictionMask = + [ + 0x0000000000000001UL, // BLOCK_4x4, + 0x0000000000000001UL, // BLOCK_4x8, + 0x0000000000000001UL, // BLOCK_8x4, + 0x0000000000000001UL, // BLOCK_8x8, + 0x0000000000000101UL, // BLOCK_8x16, + 0x0000000000000001UL, // BLOCK_16x8, + 0x0000000000000101UL, // BLOCK_16x16, + 0x0000000001010101UL, // BLOCK_16x32, + 0x0000000000000101UL, // BLOCK_32x16, + 0x0000000001010101UL, // BLOCK_32x32, + 0x0101010101010101UL, // BLOCK_32x64, + 0x0000000001010101UL, // BLOCK_64x32, + 0x0101010101010101UL // BLOCK_64x64 + ]; // 64 bit mask to shift and set for each prediction size. - private static readonly ulong[] _abovePredictionMask = { - 0x0000000000000001UL, // BLOCK_4X4 - 0x0000000000000001UL, // BLOCK_4X8 - 0x0000000000000001UL, // BLOCK_8X4 - 0x0000000000000001UL, // BLOCK_8X8 - 0x0000000000000001UL, // BLOCK_8X16, - 0x0000000000000003UL, // BLOCK_16X8 - 0x0000000000000003UL, // BLOCK_16X16 - 0x0000000000000003UL, // BLOCK_16X32, - 0x000000000000000fUL, // BLOCK_32X16, - 0x000000000000000fUL, // BLOCK_32X32, - 0x000000000000000fUL, // BLOCK_32X64, - 0x00000000000000ffUL, // BLOCK_64X32, - 0x00000000000000ffUL, // BLOCK_64X64 - }; + private static readonly ulong[] _abovePredictionMask = + [ + 0x0000000000000001UL, // BLOCK_4x4 + 0x0000000000000001UL, // BLOCK_4x8 + 0x0000000000000001UL, // BLOCK_8x4 + 0x0000000000000001UL, // BLOCK_8x8 + 0x0000000000000001UL, // BLOCK_8x16, + 0x0000000000000003UL, // BLOCK_16x8 + 0x0000000000000003UL, // BLOCK_16x16 + 0x0000000000000003UL, // BLOCK_16x32, + 0x000000000000000fUL, // BLOCK_32x16, + 0x000000000000000fUL, // BLOCK_32x32, + 0x000000000000000fUL, // BLOCK_32x64, + 0x00000000000000ffUL, // BLOCK_64x32, + 0x00000000000000ffUL // BLOCK_64x64 + ]; // 64 bit mask to shift and set for each prediction size. A bit is set for // each 8x8 block that would be in the left most block of the given block // size in the 64x64 block. - private static readonly ulong[] _sizeMask = { - 0x0000000000000001UL, // BLOCK_4X4 - 0x0000000000000001UL, // BLOCK_4X8 - 0x0000000000000001UL, // BLOCK_8X4 - 0x0000000000000001UL, // BLOCK_8X8 - 0x0000000000000101UL, // BLOCK_8X16, - 0x0000000000000003UL, // BLOCK_16X8 - 0x0000000000000303UL, // BLOCK_16X16 - 0x0000000003030303UL, // BLOCK_16X32, - 0x0000000000000f0fUL, // BLOCK_32X16, - 0x000000000f0f0f0fUL, // BLOCK_32X32, - 0x0f0f0f0f0f0f0f0fUL, // BLOCK_32X64, - 0x00000000ffffffffUL, // BLOCK_64X32, - 0xffffffffffffffffUL, // BLOCK_64X64 - }; + private static readonly ulong[] _sizeMask = + [ + 0x0000000000000001UL, // BLOCK_4x4 + 0x0000000000000001UL, // BLOCK_4x8 + 0x0000000000000001UL, // BLOCK_8x4 + 0x0000000000000001UL, // BLOCK_8x8 + 0x0000000000000101UL, // BLOCK_8x16, + 0x0000000000000003UL, // BLOCK_16x8 + 0x0000000000000303UL, // BLOCK_16x16 + 0x0000000003030303UL, // BLOCK_16x32, + 0x0000000000000f0fUL, // BLOCK_32x16, + 0x000000000f0f0f0fUL, // BLOCK_32x32, + 0x0f0f0f0f0f0f0f0fUL, // BLOCK_32x64, + 0x00000000ffffffffUL, // BLOCK_64x32, + 0xffffffffffffffffUL // BLOCK_64x64 + ]; // These are used for masking the left and above borders. -#pragma warning disable IDE0051 // Remove unused private member private const ulong LeftBorder = 0x1111111111111111UL; private const ulong AboveBorder = 0x000000ff000000ffUL; -#pragma warning restore IDE0051 // 16 bit masks for uv transform sizes. - private static readonly ushort[] _left64X64TxformMaskUv = { - 0xffff, // TX_4X4 - 0xffff, // TX_8x8 - 0x5555, // TX_16x16 - 0x1111, // TX_32x32 - }; + private static readonly ushort[] _left64X64TxformMaskUv = + [ + 0xffff, // (int)TxSize.Tx4x4 + 0xffff, // (int)TxSize.Tx8x8 + 0x5555, // (int)TxSize.Tx16x16 + 0x1111 // (int)TxSize.Tx32x32 + ]; - private static readonly ushort[] _above64X64TxformMaskUv = { - 0xffff, // TX_4X4 - 0xffff, // TX_8x8 - 0x0f0f, // TX_16x16 - 0x000f, // TX_32x32 - }; + private static readonly ushort[] _above64X64TxformMaskUv = + [ + 0xffff, // (int)TxSize.Tx4x4 + 0xffff, // (int)TxSize.Tx8x8 + 0x0f0f, // (int)TxSize.Tx16x16 + 0x000f // (int)TxSize.Tx32x32 + ]; // 16 bit left mask to shift and set for each uv prediction size. - private static readonly ushort[] _leftPredictionMaskUv = { - 0x0001, // BLOCK_4X4, - 0x0001, // BLOCK_4X8, - 0x0001, // BLOCK_8X4, - 0x0001, // BLOCK_8X8, - 0x0001, // BLOCK_8X16, - 0x0001, // BLOCK_16X8, - 0x0001, // BLOCK_16X16, - 0x0011, // BLOCK_16X32, - 0x0001, // BLOCK_32X16, - 0x0011, // BLOCK_32X32, - 0x1111, // BLOCK_32X64 - 0x0011, // BLOCK_64X32, - 0x1111, // BLOCK_64X64 - }; + private static readonly ushort[] _leftPredictionMaskUv = + [ + 0x0001, // BLOCK_4x4, + 0x0001, // BLOCK_4x8, + 0x0001, // BLOCK_8x4, + 0x0001, // BLOCK_8x8, + 0x0001, // BLOCK_8x16, + 0x0001, // BLOCK_16x8, + 0x0001, // BLOCK_16x16, + 0x0011, // BLOCK_16x32, + 0x0001, // BLOCK_32x16, + 0x0011, // BLOCK_32x32, + 0x1111, // BLOCK_32x64 + 0x0011, // BLOCK_64x32, + 0x1111 // BLOCK_64x64 + ]; // 16 bit above mask to shift and set for uv each prediction size. - private static readonly ushort[] _abovePredictionMaskUv = { - 0x0001, // BLOCK_4X4 - 0x0001, // BLOCK_4X8 - 0x0001, // BLOCK_8X4 - 0x0001, // BLOCK_8X8 - 0x0001, // BLOCK_8X16, - 0x0001, // BLOCK_16X8 - 0x0001, // BLOCK_16X16 - 0x0001, // BLOCK_16X32, - 0x0003, // BLOCK_32X16, - 0x0003, // BLOCK_32X32, - 0x0003, // BLOCK_32X64, - 0x000f, // BLOCK_64X32, - 0x000f, // BLOCK_64X64 - }; + private static readonly ushort[] _abovePredictionMaskUv = + [ + 0x0001, // BLOCK_4x4 + 0x0001, // BLOCK_4x8 + 0x0001, // BLOCK_8x4 + 0x0001, // BLOCK_8x8 + 0x0001, // BLOCK_8x16, + 0x0001, // BLOCK_16x8 + 0x0001, // BLOCK_16x16 + 0x0001, // BLOCK_16x32, + 0x0003, // BLOCK_32x16, + 0x0003, // BLOCK_32x32, + 0x0003, // BLOCK_32x64, + 0x000f, // BLOCK_64x32, + 0x000f // BLOCK_64x64 + ]; // 64 bit mask to shift and set for each uv prediction size - private static readonly ushort[] _sizeMaskUv = { - 0x0001, // BLOCK_4X4 - 0x0001, // BLOCK_4X8 - 0x0001, // BLOCK_8X4 - 0x0001, // BLOCK_8X8 - 0x0001, // BLOCK_8X16, - 0x0001, // BLOCK_16X8 - 0x0001, // BLOCK_16X16 - 0x0011, // BLOCK_16X32, - 0x0003, // BLOCK_32X16, - 0x0033, // BLOCK_32X32, - 0x3333, // BLOCK_32X64, - 0x00ff, // BLOCK_64X32, - 0xffff, // BLOCK_64X64 - }; + private static readonly ushort[] _sizeMaskUv = + [ + 0x0001, // BLOCK_4x4 + 0x0001, // BLOCK_4x8 + 0x0001, // BLOCK_8x4 + 0x0001, // BLOCK_8x8 + 0x0001, // BLOCK_8x16, + 0x0001, // BLOCK_16x8 + 0x0001, // BLOCK_16x16 + 0x0011, // BLOCK_16x32, + 0x0003, // BLOCK_32x16, + 0x0033, // BLOCK_32x32, + 0x3333, // BLOCK_32x64, + 0x00ff, // BLOCK_64x32, + 0xffff // BLOCK_64x64 + ]; -#pragma warning disable IDE0051 // Remove unused private member private const ushort LeftBorderUv = 0x1111; private const ushort AboveBorderUv = 0x000f; -#pragma warning restore IDE0051 - private static readonly int[] _modeLfLut = { + private static readonly int[] _modeLfLut = + [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // INTRA_MODES - 1, 1, 0, 1, // INTER_MODES (ZEROMV == 0) - }; + 1, 1, 0, 1 // INTER_MODES (ZEROMV == 0) + ]; private static byte GetFilterLevel(ref LoopFilterInfoN lfiN, ref ModeInfo mi) { return lfiN.Lvl[mi.SegmentId][mi.RefFrame[0]][_modeLfLut[(int)mi.Mode]]; } - private static ref LoopFilterMask GetLfm(ref Types.LoopFilter lf, int miRow, int miCol) + private static Span GetLfm(ref Types.LoopFilter lf, int miRow, int miCol) { - return ref lf.Lfm[(miCol >> 3) + ((miRow >> 3) * lf.LfmStride)]; + return lf.Lfm.AsSpan().Slice((miCol >> 3) + ((miRow >> 3) * lf.LfmStride)); } // 8x8 blocks in a superblock. A "1" represents the first block in a 16x16 // or greater area. - private static readonly byte[][] _firstBlockIn16X16 = { - new byte[] { 1, 0, 1, 0, 1, 0, 1, 0 }, new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 1, 0, 1, 0, 1, 0, 1, 0 }, new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 1, 0, 1, 0, 1, 0, 1, 0 }, new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 1, 0, 1, 0, 1, 0, 1, 0 }, new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, - }; + private static readonly byte[][] _firstBlockIn16X16 = + [ + [1, 0, 1, 0, 1, 0, 1, 0], [0, 0, 0, 0, 0, 0, 0, 0], + [1, 0, 1, 0, 1, 0, 1, 0], [0, 0, 0, 0, 0, 0, 0, 0], + [1, 0, 1, 0, 1, 0, 1, 0], [0, 0, 0, 0, 0, 0, 0, 0], + [1, 0, 1, 0, 1, 0, 1, 0], [0, 0, 0, 0, 0, 0, 0, 0] + ]; // This function sets up the bit masks for a block represented // by miRow, miCol in a 64x64 region. @@ -238,15 +358,15 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 ref LoopFilterInfoN lfiN = ref cm.LfInfo; int filterLevel = GetFilterLevel(ref lfiN, ref mi); TxSize txSizeUv = Luts.UvTxsizeLookup[(int)blockSize][(int)txSizeY][1][1]; - ref LoopFilterMask lfm = ref GetLfm(ref cm.Lf, miRow, miCol); + ref LoopFilterMask lfm = ref GetLfm(ref cm.Lf, miRow, miCol)[0]; ref ulong leftY = ref lfm.LeftY[(int)txSizeY]; ref ulong aboveY = ref lfm.AboveY[(int)txSizeY]; - ref ulong int4X4Y = ref lfm.Int4x4Y; + ref ulong int4X4Y = ref lfm.Int4X4Y; ref ushort leftUv = ref lfm.LeftUv[(int)txSizeUv]; ref ushort aboveUv = ref lfm.AboveUv[(int)txSizeUv]; - ref ushort int4X4Uv = ref lfm.Int4x4Uv; - int rowInSb = (miRow & 7); - int colInSb = (miCol & 7); + ref ushort int4X4Uv = ref lfm.Int4X4Uv; + int rowInSb = miRow & 7; + int colInSb = miCol & 7; int shiftY = colInSb + (rowInSb << 3); int shiftUv = (colInSb >> 1) + ((rowInSb >> 1) << 2); int buildUv = _firstBlockIn16X16[rowInSb][colInSb]; @@ -257,10 +377,10 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 } int index = shiftY; - int i; - for (i = 0; i < bh; i++) + + for (int i = 0; i < bh; i++) { - MemoryMarshal.CreateSpan(ref lfm.LflY[index], 64 - index)[..bw].Fill((byte)filterLevel); + MemoryMarshal.CreateSpan(ref lfm.LflY[index], 64 - index).Slice(0, bw).Fill((byte)filterLevel); index += 8; } @@ -307,22 +427,156 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 // Try to determine what to do with the internal 4x4 block boundaries. These // differ from the 4x4 boundaries on the outside edge of an 8x8 in that the // internal ones can be skipped and don't depend on the prediction block size. - if (txSizeY == TxSize.Tx4x4) + if (txSizeY == TxSize.Tx4X4) { int4X4Y |= _sizeMask[(int)blockSize] << shiftY; } - if (buildUv != 0 && txSizeUv == TxSize.Tx4x4) + if (buildUv != 0 && txSizeUv == TxSize.Tx4X4) { int4X4Uv |= (ushort)((_sizeMaskUv[(int)blockSize] & 0xffff) << shiftUv); } } + private static void AdjustMask(ref Vp9Common cm, int miRow, int miCol, ref LoopFilterMask lfm) + { + const ulong LeftBorder = 0x1111111111111111UL; + const ulong AboveBorder = 0x000000ff000000ffUL; + const ushort LeftBorderUv = 0x1111; + const ushort AboveBorderUv = 0x000f; + + + // The largest loopfilter we have is 16x16 so we use the 16x16 mask + // for 32x32 transforms also. + lfm.LeftY[(int)TxSize.Tx16X16] |= lfm.LeftY[(int)TxSize.Tx32X32]; + lfm.AboveY[(int)TxSize.Tx16X16] |= lfm.AboveY[(int)TxSize.Tx32X32]; + lfm.LeftUv[(int)TxSize.Tx16X16] |= lfm.LeftUv[(int)TxSize.Tx32X32]; + lfm.AboveUv[(int)TxSize.Tx16X16] |= lfm.AboveUv[(int)TxSize.Tx32X32]; + + // We do at least 8 tap filter on every 32x32 even if the transform size + // is 4x4. So if the 4x4 is set on a border pixel add it to the 8x8 and + // remove it from the 4x4. + lfm.LeftY[(int)TxSize.Tx8X8] |= lfm.LeftY[(int)TxSize.Tx4X4] & LeftBorder; + lfm.LeftY[(int)TxSize.Tx4X4] &= ~LeftBorder; + lfm.AboveY[(int)TxSize.Tx8X8] |= lfm.AboveY[(int)TxSize.Tx4X4] & AboveBorder; + lfm.AboveY[(int)TxSize.Tx4X4] &= ~AboveBorder; + lfm.LeftUv[(int)TxSize.Tx8X8] |= (ushort)(lfm.LeftUv[(int)TxSize.Tx4X4] & LeftBorderUv); + lfm.LeftUv[(int)TxSize.Tx4X4] &= unchecked((ushort)~LeftBorderUv); + lfm.AboveUv[(int)TxSize.Tx8X8] |= (ushort)(lfm.AboveUv[(int)TxSize.Tx4X4] & AboveBorderUv); + lfm.AboveUv[(int)TxSize.Tx4X4] &= unchecked((ushort)~AboveBorderUv); + + // We do some special edge handling. + if (miRow + Constants.MiBlockSize > cm.MiRows) + { + int rows = cm.MiRows - miRow; + + // Each pixel inside the border gets a 1, + ulong maskY = (1UL << (rows << 3)) - 1; + ushort maskUv = (ushort)((1 << (((rows + 1) >> 1) << 2)) - 1); + + // Remove values completely outside our border. + for (int i = 0; i < (int)TxSize.Tx32X32; i++) + { + lfm.LeftY[i] &= maskY; + lfm.AboveY[i] &= maskY; + lfm.LeftUv[i] &= maskUv; + lfm.AboveUv[i] &= maskUv; + } + + lfm.Int4X4Y &= maskY; + lfm.Int4X4Uv &= maskUv; + + // We don't apply a wide loop filter on the last uv block row. If set + // apply the shorter one instead. + if (rows == 1) + { + lfm.AboveUv[(int)TxSize.Tx8X8] |= lfm.AboveUv[(int)TxSize.Tx16X16]; + lfm.AboveUv[(int)TxSize.Tx16X16] = 0; + } + + if (rows == 5) + { + lfm.AboveUv[(int)TxSize.Tx8X8] |= (ushort)(lfm.AboveUv[(int)TxSize.Tx16X16] & 0xff00); + lfm.AboveUv[(int)TxSize.Tx16X16] &= (ushort)~(lfm.AboveUv[(int)TxSize.Tx16X16] & 0xff00); + } + } + + if (miCol + Constants.MiBlockSize > cm.MiCols) + { + int columns = cm.MiCols - miCol; + + // Each pixel inside the border gets a 1, the multiply copies the border + // to where we need it. + ulong maskY = ((1UL << columns) - 1) * 0x0101010101010101UL; + ushort maskUv = (ushort)(((1 << ((columns + 1) >> 1)) - 1) * 0x1111); + + // Internal edges are not applied on the last column of the image so + // we mask 1 more for the internal edges + ushort maskUvInt = (ushort)(((1 << (columns >> 1)) - 1) * 0x1111); + + // Remove the bits outside the image edge. + for (int i = 0; i < (int)TxSize.Tx32X32; i++) + { + lfm.LeftY[i] &= maskY; + lfm.AboveY[i] &= maskY; + lfm.LeftUv[i] &= maskUv; + lfm.AboveUv[i] &= maskUv; + } + + lfm.Int4X4Y &= maskY; + lfm.Int4X4Uv &= maskUvInt; + + // We don't apply a wide loop filter on the last uv column. If set + // apply the shorter one instead. + if (columns == 1) + { + lfm.LeftUv[(int)TxSize.Tx8X8] |= lfm.LeftUv[(int)TxSize.Tx16X16]; + lfm.LeftUv[(int)TxSize.Tx16X16] = 0; + } + + if (columns == 5) + { + lfm.LeftUv[(int)TxSize.Tx8X8] |= (ushort)(lfm.LeftUv[(int)TxSize.Tx16X16] & 0xcccc); + lfm.LeftUv[(int)TxSize.Tx16X16] &= (ushort)~(lfm.LeftUv[(int)TxSize.Tx16X16] & 0xcccc); + } + } + + // We don't apply a loop filter on the first column in the image, mask that + // out. + if (miCol == 0) + { + for (int i = 0; i < (int)TxSize.Tx32X32; i++) + { + lfm.LeftY[i] &= 0xfefefefefefefefeUL; + lfm.LeftUv[i] &= 0xeeee; + } + } + + // Assert if we try to apply 2 different loop filters at the same position. + Debug.Assert((lfm.LeftY[(int)TxSize.Tx16X16] & lfm.LeftY[(int)TxSize.Tx8X8]) == 0); + Debug.Assert((lfm.LeftY[(int)TxSize.Tx16X16] & lfm.LeftY[(int)TxSize.Tx4X4]) == 0); + Debug.Assert((lfm.LeftY[(int)TxSize.Tx8X8] & lfm.LeftY[(int)TxSize.Tx4X4]) == 0); + Debug.Assert((lfm.Int4X4Y & lfm.LeftY[(int)TxSize.Tx16X16]) == 0); + Debug.Assert((lfm.LeftUv[(int)TxSize.Tx16X16] & lfm.LeftUv[(int)TxSize.Tx8X8]) == 0); + Debug.Assert((lfm.LeftUv[(int)TxSize.Tx16X16] & lfm.LeftUv[(int)TxSize.Tx4X4]) == 0); + Debug.Assert((lfm.LeftUv[(int)TxSize.Tx8X8] & lfm.LeftUv[(int)TxSize.Tx4X4]) == 0); + Debug.Assert((lfm.Int4X4Uv & lfm.LeftUv[(int)TxSize.Tx16X16]) == 0); + Debug.Assert((lfm.AboveY[(int)TxSize.Tx16X16] & lfm.AboveY[(int)TxSize.Tx8X8]) == 0); + Debug.Assert((lfm.AboveY[(int)TxSize.Tx16X16] & lfm.AboveY[(int)TxSize.Tx4X4]) == 0); + Debug.Assert((lfm.AboveY[(int)TxSize.Tx8X8] & lfm.AboveY[(int)TxSize.Tx4X4]) == 0); + Debug.Assert((lfm.Int4X4Y & lfm.AboveY[(int)TxSize.Tx16X16]) == 0); + Debug.Assert((lfm.AboveUv[(int)TxSize.Tx16X16] & lfm.AboveUv[(int)TxSize.Tx8X8]) == 0); + Debug.Assert((lfm.AboveUv[(int)TxSize.Tx16X16] & lfm.AboveUv[(int)TxSize.Tx4X4]) == 0); + Debug.Assert((lfm.AboveUv[(int)TxSize.Tx8X8] & lfm.AboveUv[(int)TxSize.Tx4X4]) == 0); + Debug.Assert((lfm.Int4X4Uv & lfm.AboveUv[(int)TxSize.Tx16X16]) == 0); + } + public static unsafe void ResetLfm(ref Vp9Common cm) { if (cm.Lf.FilterLevel != 0) { - MemoryUtil.Fill(cm.Lf.Lfm.ToPointer(), new LoopFilterMask(), ((cm.MiRows + (Constants.MiBlockSize - 1)) >> 3) * cm.Lf.LfmStride); + MemoryUtil.Fill(cm.Lf.Lfm.ToPointer(), new LoopFilterMask(), + ((cm.MiRows + (Constants.MiBlockSize - 1)) >> 3) * cm.Lf.LfmStride); } } @@ -338,9 +592,9 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 if (sharpnessLvl > 0) { - if (blockInsideLimit > (9 - sharpnessLvl)) + if (blockInsideLimit > 9 - sharpnessLvl) { - blockInsideLimit = (9 - sharpnessLvl); + blockInsideLimit = 9 - sharpnessLvl; } } @@ -350,7 +604,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 } lfi.Lfthr[lvl].Lim.AsSpan().Fill((byte)blockInsideLimit); - lfi.Lfthr[lvl].Mblim.AsSpan().Fill((byte)(2 * (lvl + 2) + blockInsideLimit)); + lfi.Lfthr[lvl].Mblim.AsSpan().Fill((byte)((2 * (lvl + 2)) + blockInsideLimit)); } } @@ -375,10 +629,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 for (segId = 0; segId < Constants.MaxSegments; segId++) { int lvlSeg = defaultFiltLvl; - if (seg.IsSegFeatureActive(segId, SegLvlFeatures.SegLvlAltLf) != 0) + if (seg.IsSegFeatureActive(segId, SegLvlFeatures.AltLf) != 0) { - int data = seg.GetSegData(segId, SegLvlFeatures.SegLvlAltLf); - lvlSeg = Math.Clamp(seg.AbsDelta == Constants.SegmentAbsData ? data : defaultFiltLvl + data, 0, MaxLoopFilter); + int data = seg.GetSegData(segId, SegLvlFeatures.AltLf); + lvlSeg = Math.Clamp(seg.AbsDelta == Constants.SegmentAbsData ? data : defaultFiltLvl + data, 0, + MaxLoopFilter); } if (!lf.ModeRefDeltaEnabled) @@ -390,19 +645,1322 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 else { int refr, mode; - int intraLvl = lvlSeg + lf.RefDeltas[Constants.IntraFrame] * scale; + int intraLvl = lvlSeg + (lf.RefDeltas[Constants.IntraFrame] * scale); lfi.Lvl[segId][Constants.IntraFrame][0] = (byte)Math.Clamp(intraLvl, 0, MaxLoopFilter); for (refr = Constants.LastFrame; refr < Constants.MaxRefFrames; ++refr) { for (mode = 0; mode < MaxModeLfDeltas; ++mode) { - int interLvl = lvlSeg + lf.RefDeltas[refr] * scale + lf.ModeDeltas[mode] * scale; + int interLvl = lvlSeg + (lf.RefDeltas[refr] * scale) + (lf.ModeDeltas[mode] * scale); lfi.Lvl[segId][refr][mode] = (byte)Math.Clamp(interLvl, 0, MaxLoopFilter); } } } } } + + private static void FilterSelectivelyVertRow2( + int subsamplingFactor, + ArrayPtr s, + int pitch, + uint mask16X16, + uint mask8X8, + uint mask4X4, + uint mask4X4Int, + ReadOnlySpan lfthr, + ReadOnlySpan lfl) + { + uint dualMaskCutoff = subsamplingFactor != 0 ? 0xffu : 0xffffu; + int lflForward = subsamplingFactor != 0 ? 4 : 8; + uint dualOne = 1u | (1u << lflForward); + Span> ss = stackalloc ArrayPtr[2]; + Span lfis = stackalloc LoopFilterThresh[2]; + ss[0] = s; + + for (uint mask = (mask16X16 | mask8X8 | mask4X4 | mask4X4Int) & dualMaskCutoff; + mask != 0; + mask = (mask & ~dualOne) >> 1) + { + if ((mask & dualOne) != 0) + { + lfis[0] = lfthr[lfl[0]]; + lfis[1] = lfthr[lfl[lflForward]]; + ss[1] = ss[0].Slice(8 * pitch); + + if ((mask16X16 & dualOne) != 0) + { + if ((mask16X16 & dualOne) == dualOne) + { + LoopFilterAuto.LpfVertical16Dual(ss[0], pitch, lfis[0].Mblim.AsSpan(), lfis[0].Lim.AsSpan(), + lfis[0].HevThr.AsSpan()); + } + else + { + ref LoopFilterThresh lfi = ref lfis[(mask16X16 & 1) == 0 ? 1 : 0]; + LoopFilterAuto.LpfVertical16(ss[(mask16X16 & 1) == 0 ? 1 : 0], pitch, lfi.Mblim.AsSpan(), + lfi.Lim.AsSpan(), lfi.HevThr.AsSpan()); + } + } + + if ((mask8X8 & dualOne) != 0) + { + if ((mask8X8 & dualOne) == dualOne) + { + LoopFilterAuto.LpfVertical8Dual( + ss[0], + pitch, + lfis[0].Mblim.AsSpan(), + lfis[0].Lim.AsSpan(), + lfis[0].HevThr.AsSpan(), + lfis[1].Mblim.AsSpan(), + lfis[1].Lim.AsSpan(), + lfis[1].HevThr.AsSpan()); + } + else + { + ref LoopFilterThresh lfi = ref lfis[(mask8X8 & 1) == 0 ? 1 : 0]; + LoopFilterAuto.LpfVertical8( + ss[(mask8X8 & 1) == 0 ? 1 : 0], + pitch, + lfi.Mblim.AsSpan(), + lfi.Lim.AsSpan(), + lfi.HevThr.AsSpan()); + } + } + + if ((mask4X4 & dualOne) != 0) + { + if ((mask4X4 & dualOne) == dualOne) + { + LoopFilterAuto.LpfVertical4Dual( + ss[0], + pitch, + lfis[0].Mblim.AsSpan(), + lfis[0].Lim.AsSpan(), + lfis[0].HevThr.AsSpan(), + lfis[1].Mblim.AsSpan(), + lfis[1].Lim.AsSpan(), + lfis[1].HevThr.AsSpan()); + } + else + { + ref LoopFilterThresh lfi = ref lfis[(mask4X4 & 1) == 0 ? 1 : 0]; + LoopFilterAuto.LpfVertical4(ss[(mask4X4 & 1) == 0 ? 1 : 0], pitch, lfi.Mblim.AsSpan(), + lfi.Lim.AsSpan(), lfi.HevThr.AsSpan()); + } + } + + if ((mask4X4Int & dualOne) != 0) + { + if ((mask4X4Int & dualOne) == dualOne) + { + LoopFilterAuto.LpfVertical4Dual( + ss[0].Slice(4), + pitch, + lfis[0].Mblim.AsSpan(), + lfis[0].Lim.AsSpan(), + lfis[0].HevThr.AsSpan(), + lfis[1].Mblim.AsSpan(), + lfis[1].Lim.AsSpan(), + lfis[1].HevThr.AsSpan()); + } + else + { + ref LoopFilterThresh lfi = ref lfis[(mask4X4Int & 1) == 0 ? 1 : 0]; + LoopFilterAuto.LpfVertical4(ss[(mask4X4Int & 1) == 0 ? 1 : 0].Slice(4), pitch, + lfi.Mblim.AsSpan(), lfi.Lim.AsSpan(), lfi.HevThr.AsSpan()); + } + } + } + + ss[0] = ss[0].Slice(8); + lfl = lfl.Slice(1); + mask16X16 >>= 1; + mask8X8 >>= 1; + mask4X4 >>= 1; + mask4X4Int >>= 1; + } + } + + private static void HighbdFilterSelectivelyVertRow2( + int subsamplingFactor, + ArrayPtr s, + int pitch, + uint mask16X16, + uint mask8X8, + uint mask4X4, + uint mask4X4Int, + ReadOnlySpan lfthr, + ReadOnlySpan lfl, + int bd) + { + uint dualMaskCutoff = subsamplingFactor != 0 ? 0xffu : 0xffffu; + int lflForward = subsamplingFactor != 0 ? 4 : 8; + uint dualOne = 1u | (1u << lflForward); + Span> ss = stackalloc ArrayPtr[2]; + Span lfis = stackalloc LoopFilterThresh[2]; + ss[0] = s; + + for (uint mask = (mask16X16 | mask8X8 | mask4X4 | mask4X4Int) & dualMaskCutoff; + mask != 0; + mask = (mask & ~dualOne) >> 1) + { + if ((mask & dualOne) != 0) + { + lfis[0] = lfthr[lfl[0]]; + lfis[1] = lfthr[lfl[lflForward]]; + ss[1] = ss[0].Slice(8 * pitch); + + if ((mask16X16 & dualOne) != 0) + { + if ((mask16X16 & dualOne) == dualOne) + { + LoopFilterScalar.HighBdLpfVertical16Dual(ss[0], pitch, lfis[0].Mblim[0], lfis[0].Lim[0], + lfis[0].HevThr[0], bd); + } + else + { + ref LoopFilterThresh lfi = ref lfis[(mask16X16 & 1) == 0 ? 1 : 0]; + LoopFilterScalar.HighBdLpfVertical16(ss[(mask16X16 & 1) == 0 ? 1 : 0], pitch, lfi.Mblim[0], + lfi.Lim[0], lfi.HevThr[0], bd); + } + } + + if ((mask8X8 & dualOne) != 0) + { + if ((mask8X8 & dualOne) == dualOne) + { + LoopFilterScalar.HighBdLpfVertical8Dual( + ss[0], + pitch, + lfis[0].Mblim[0], + lfis[0].Lim[0], + lfis[0].HevThr[0], + lfis[1].Mblim[0], + lfis[1].Lim[0], + lfis[1].HevThr[0], + bd); + } + else + { + ref LoopFilterThresh lfi = ref lfis[(mask8X8 & 1) == 0 ? 1 : 0]; + LoopFilterScalar.HighBdLpfVertical8( + ss[(mask8X8 & 1) == 0 ? 1 : 0], + pitch, + lfi.Mblim[0], + lfi.Lim[0], + lfi.HevThr[0], + bd); + } + } + + if ((mask4X4 & dualOne) != 0) + { + if ((mask4X4 & dualOne) == dualOne) + { + LoopFilterScalar.HighBdLpfVertical4Dual( + ss[0], + pitch, + lfis[0].Mblim[0], + lfis[0].Lim[0], + lfis[0].HevThr[0], + lfis[1].Mblim[0], + lfis[1].Lim[0], + lfis[1].HevThr[0], + bd); + } + else + { + ref LoopFilterThresh lfi = ref lfis[(mask4X4 & 1) == 0 ? 1 : 0]; + LoopFilterScalar.HighBdLpfVertical4(ss[(mask4X4 & 1) == 0 ? 1 : 0], pitch, lfi.Mblim[0], + lfi.Lim[0], lfi.HevThr[0], bd); + } + } + + if ((mask4X4Int & dualOne) != 0) + { + if ((mask4X4Int & dualOne) == dualOne) + { + LoopFilterScalar.HighBdLpfVertical4Dual( + ss[0].Slice(4), + pitch, + lfis[0].Mblim[0], + lfis[0].Lim[0], + lfis[0].HevThr[0], + lfis[1].Mblim[0], + lfis[1].Lim[0], + lfis[1].HevThr[0], + bd); + } + else + { + ref LoopFilterThresh lfi = ref lfis[(mask4X4Int & 1) == 0 ? 1 : 0]; + LoopFilterScalar.HighBdLpfVertical4(ss[(mask4X4Int & 1) == 0 ? 1 : 0].Slice(4), pitch, + lfi.Mblim[0], lfi.Lim[0], lfi.HevThr[0], bd); + } + } + } + + ss[0] = ss[0].Slice(8); + lfl = lfl.Slice(1); + mask16X16 >>= 1; + mask8X8 >>= 1; + mask4X4 >>= 1; + mask4X4Int >>= 1; + } + } + + private static void FilterSelectivelyHoriz( + ArrayPtr s, + int pitch, + uint mask16X16, + uint mask8X8, + uint mask4X4, + uint mask4X4Int, + ReadOnlySpan lfthr, + ReadOnlySpan lfl) + { + int count; + + for (uint mask = mask16X16 | mask8X8 | mask4X4 | mask4X4Int; mask != 0; mask >>= count) + { + count = 1; + if ((mask & 1) != 0) + { + LoopFilterThresh lfi = lfthr[lfl[0]]; + + if ((mask16X16 & 1) != 0) + { + if ((mask16X16 & 3) == 3) + { + LoopFilterAuto.LpfHorizontal16Dual(s, pitch, lfi.Mblim.AsSpan(), lfi.Lim.AsSpan(), + lfi.HevThr.AsSpan()); + count = 2; + } + else + { + LoopFilterAuto.LpfHorizontal16(s, pitch, lfi.Mblim.AsSpan(), lfi.Lim.AsSpan(), + lfi.HevThr.AsSpan()); + } + } + else if ((mask8X8 & 1) != 0) + { + if ((mask8X8 & 3) == 3) + { + // Next block's thresholds. + LoopFilterThresh lfin = lfthr[lfl[1]]; + + LoopFilterAuto.LpfHorizontal8Dual( + s, + pitch, + lfi.Mblim.AsSpan(), + lfi.Lim.AsSpan(), + lfi.HevThr.AsSpan(), + lfin.Mblim.AsSpan(), + lfin.Lim.AsSpan(), + lfin.HevThr.AsSpan()); + + if ((mask4X4Int & 3) == 3) + { + LoopFilterAuto.LpfHorizontal4Dual( + s.Slice(4 * pitch), + pitch, + lfi.Mblim.AsSpan(), + lfi.Lim.AsSpan(), + lfi.HevThr.AsSpan(), + lfin.Mblim.AsSpan(), + lfin.Lim.AsSpan(), + lfin.HevThr.AsSpan()); + } + else if ((mask4X4Int & 1) != 0) + { + LoopFilterAuto.LpfHorizontal4(s.Slice(4 * pitch), pitch, lfi.Mblim.AsSpan(), + lfi.Lim.AsSpan(), lfi.HevThr.AsSpan()); + } + else if ((mask4X4Int & 2) != 0) + { + LoopFilterAuto.LpfHorizontal4(s.Slice(8 + (4 * pitch)), pitch, lfin.Mblim.AsSpan(), + lfin.Lim.AsSpan(), lfin.HevThr.AsSpan()); + } + + count = 2; + } + else + { + LoopFilterAuto.LpfHorizontal8(s, pitch, lfi.Mblim.AsSpan(), lfi.Lim.AsSpan(), + lfi.HevThr.AsSpan()); + + if ((mask4X4Int & 1) != 0) + { + LoopFilterAuto.LpfHorizontal4(s.Slice(4 * pitch), pitch, lfi.Mblim.AsSpan(), + lfi.Lim.AsSpan(), lfi.HevThr.AsSpan()); + } + } + } + else if ((mask4X4 & 1) != 0) + { + if ((mask4X4 & 3) == 3) + { + // Next block's thresholds. + LoopFilterThresh lfin = lfthr[lfl[1]]; + + LoopFilterAuto.LpfHorizontal4Dual( + s, + pitch, + lfi.Mblim.AsSpan(), + lfi.Lim.AsSpan(), + lfi.HevThr.AsSpan(), + lfin.Mblim.AsSpan(), + lfin.Lim.AsSpan(), + lfin.HevThr.AsSpan()); + + if ((mask4X4Int & 3) == 3) + { + LoopFilterAuto.LpfHorizontal4Dual( + s.Slice(4 * pitch), + pitch, + lfi.Mblim.AsSpan(), + lfi.Lim.AsSpan(), + lfi.HevThr.AsSpan(), + lfin.Mblim.AsSpan(), + lfin.Lim.AsSpan(), + lfin.HevThr.AsSpan()); + } + else if ((mask4X4Int & 1) != 0) + { + LoopFilterAuto.LpfHorizontal4(s.Slice(4 * pitch), pitch, lfi.Mblim.AsSpan(), + lfi.Lim.AsSpan(), lfi.HevThr.AsSpan()); + } + else if ((mask4X4Int & 2) != 0) + { + LoopFilterAuto.LpfHorizontal4(s.Slice(8 + (4 * pitch)), pitch, lfin.Mblim.AsSpan(), + lfin.Lim.AsSpan(), lfin.HevThr.AsSpan()); + } + + count = 2; + } + else + { + LoopFilterAuto.LpfHorizontal4(s, pitch, lfi.Mblim.AsSpan(), lfi.Lim.AsSpan(), + lfi.HevThr.AsSpan()); + + if ((mask4X4Int & 1) != 0) + { + LoopFilterAuto.LpfHorizontal4(s.Slice(4 * pitch), pitch, lfi.Mblim.AsSpan(), + lfi.Lim.AsSpan(), lfi.HevThr.AsSpan()); + } + } + } + else + { + LoopFilterAuto.LpfHorizontal4(s.Slice(4 * pitch), pitch, lfi.Mblim.AsSpan(), lfi.Lim.AsSpan(), + lfi.HevThr.AsSpan()); + } + } + + s = s.Slice(8 * count); + lfl = lfl.Slice(count); + mask16X16 >>= count; + mask8X8 >>= count; + mask4X4 >>= count; + mask4X4Int >>= count; + } + } + + private static void HighbdFilterSelectivelyHoriz( + ArrayPtr s, + int pitch, + uint mask16X16, + uint mask8X8, + uint mask4X4, + uint mask4X4Int, + ReadOnlySpan lfthr, + ReadOnlySpan lfl, + int bd) + { + int count; + + for (uint mask = mask16X16 | mask8X8 | mask4X4 | mask4X4Int; mask != 0; mask >>= count) + { + count = 1; + if ((mask & 1) != 0) + { + LoopFilterThresh lfi = lfthr[lfl[0]]; + + if ((mask16X16 & 1) != 0) + { + if ((mask16X16 & 3) == 3) + { + LoopFilterScalar.HighBdLpfHorizontal16Dual(s, pitch, lfi.Mblim[0], lfi.Lim[0], + lfi.HevThr[0], bd); + count = 2; + } + else + { + LoopFilterScalar.HighBdLpfHorizontal16(s, pitch, lfi.Mblim[0], lfi.Lim[0], lfi.HevThr[0], + bd); + } + } + else if ((mask8X8 & 1) != 0) + { + if ((mask8X8 & 3) == 3) + { + // Next block's thresholds. + LoopFilterThresh lfin = lfthr[lfl[1]]; + + LoopFilterScalar.HighBdLpfHorizontal8Dual( + s, + pitch, + lfi.Mblim[0], + lfi.Lim[0], + lfi.HevThr[0], + lfin.Mblim[0], + lfin.Lim[0], + lfin.HevThr[0], + bd); + + if ((mask4X4Int & 3) == 3) + { + LoopFilterScalar.HighBdLpfHorizontal4Dual( + s.Slice(4 * pitch), + pitch, + lfi.Mblim[0], + lfi.Lim[0], + lfi.HevThr[0], + lfin.Mblim[0], + lfin.Lim[0], + lfin.HevThr[0], + bd); + } + else if ((mask4X4Int & 1) != 0) + { + LoopFilterScalar.HighBdLpfHorizontal4(s.Slice(4 * pitch), pitch, lfi.Mblim[0], + lfi.Lim[0], lfi.HevThr[0], bd); + } + else if ((mask4X4Int & 2) != 0) + { + LoopFilterScalar.HighBdLpfHorizontal4(s.Slice(8 + (4 * pitch)), pitch, lfin.Mblim[0], + lfin.Lim[0], lfin.HevThr[0], bd); + } + + count = 2; + } + else + { + LoopFilterScalar.HighBdLpfHorizontal8(s, pitch, lfi.Mblim[0], lfi.Lim[0], lfi.HevThr[0], + bd); + + if ((mask4X4Int & 1) != 0) + { + LoopFilterScalar.HighBdLpfHorizontal4(s.Slice(4 * pitch), pitch, lfi.Mblim[0], + lfi.Lim[0], lfi.HevThr[0], bd); + } + } + } + else if ((mask4X4 & 1) != 0) + { + if ((mask4X4 & 3) == 3) + { + // Next block's thresholds. + LoopFilterThresh lfin = lfthr[lfl[1]]; + + LoopFilterScalar.HighBdLpfHorizontal4Dual( + s, + pitch, + lfi.Mblim[0], + lfi.Lim[0], + lfi.HevThr[0], + lfin.Mblim[0], + lfin.Lim[0], + lfin.HevThr[0], + bd); + + if ((mask4X4Int & 3) == 3) + { + LoopFilterScalar.HighBdLpfHorizontal4Dual( + s.Slice(4 * pitch), + pitch, + lfi.Mblim[0], + lfi.Lim[0], + lfi.HevThr[0], + lfin.Mblim[0], + lfin.Lim[0], + lfin.HevThr[0], + bd); + } + else if ((mask4X4Int & 1) != 0) + { + LoopFilterScalar.HighBdLpfHorizontal4(s.Slice(4 * pitch), pitch, lfi.Mblim[0], + lfi.Lim[0], lfi.HevThr[0], bd); + } + else if ((mask4X4Int & 2) != 0) + { + LoopFilterScalar.HighBdLpfHorizontal4(s.Slice(8 + (4 * pitch)), pitch, lfin.Mblim[0], + lfin.Lim[0], lfin.HevThr[0], bd); + } + + count = 2; + } + else + { + LoopFilterScalar.HighBdLpfHorizontal4(s, pitch, lfi.Mblim[0], lfi.Lim[0], lfi.HevThr[0], + bd); + + if ((mask4X4Int & 1) != 0) + { + LoopFilterScalar.HighBdLpfHorizontal4(s.Slice(4 * pitch), pitch, lfi.Mblim[0], + lfi.Lim[0], lfi.HevThr[0], bd); + } + } + } + else + { + LoopFilterScalar.HighBdLpfHorizontal4(s.Slice(4 * pitch), pitch, lfi.Mblim[0], lfi.Lim[0], + lfi.HevThr[0], bd); + } + } + + s = s.Slice(8 * count); + lfl = lfl.Slice(count); + mask16X16 >>= count; + mask8X8 >>= count; + mask4X4 >>= count; + mask4X4Int >>= count; + } + } + + private static void FilterSelectivelyVert( + ArrayPtr s, + int pitch, + uint mask16X16, + uint mask8X8, + uint mask4X4, + uint mask4X4Int, + ReadOnlySpan lfthr, + ReadOnlySpan lfl) + { + for (uint mask = mask16X16 | mask8X8 | mask4X4 | mask4X4Int; mask != 0; mask >>= 1) + { + LoopFilterThresh lfi = lfthr[lfl[0]]; + + if ((mask & 1) != 0) + { + if ((mask16X16 & 1) != 0) + { + LoopFilterAuto.LpfVertical16(s, pitch, lfi.Mblim.AsSpan(), lfi.Lim.AsSpan(), + lfi.HevThr.AsSpan()); + } + else if ((mask8X8 & 1) != 0) + { + LoopFilterAuto.LpfVertical8(s, pitch, lfi.Mblim.AsSpan(), lfi.Lim.AsSpan(), + lfi.HevThr.AsSpan()); + } + else if ((mask4X4 & 1) != 0) + { + LoopFilterAuto.LpfVertical4(s, pitch, lfi.Mblim.AsSpan(), lfi.Lim.AsSpan(), + lfi.HevThr.AsSpan()); + } + } + + if ((mask4X4Int & 1) != 0) + { + LoopFilterAuto.LpfVertical4(s.Slice(4), pitch, lfi.Mblim.AsSpan(), lfi.Lim.AsSpan(), + lfi.HevThr.AsSpan()); + } + + s = s.Slice(8); + lfl = lfl.Slice(1); + mask16X16 >>= 1; + mask8X8 >>= 1; + mask4X4 >>= 1; + mask4X4Int >>= 1; + } + } + + private static void HighbdFilterSelectivelyVert( + ArrayPtr s, + int pitch, + uint mask16X16, + uint mask8X8, + uint mask4X4, + uint mask4X4Int, + ReadOnlySpan lfthr, + ReadOnlySpan lfl, + int bd) + { + for (uint mask = mask16X16 | mask8X8 | mask4X4 | mask4X4Int; mask != 0; mask >>= 1) + { + LoopFilterThresh lfi = lfthr[lfl[0]]; + + if ((mask & 1) != 0) + { + if ((mask16X16 & 1) != 0) + { + LoopFilterScalar.HighBdLpfVertical16(s, pitch, lfi.Mblim[0], lfi.Lim[0], lfi.HevThr[0], bd); + } + else if ((mask8X8 & 1) != 0) + { + LoopFilterScalar.HighBdLpfVertical8(s, pitch, lfi.Mblim[0], lfi.Lim[0], lfi.HevThr[0], bd); + } + else if ((mask4X4 & 1) != 0) + { + LoopFilterScalar.HighBdLpfVertical4(s, pitch, lfi.Mblim[0], lfi.Lim[0], lfi.HevThr[0], bd); + } + } + + if ((mask4X4Int & 1) != 0) + { + LoopFilterScalar.HighBdLpfVertical4(s.Slice(4), pitch, lfi.Mblim[0], lfi.Lim[0], lfi.HevThr[0], bd); + } + + s = s.Slice(8); + lfl = lfl.Slice(1); + mask16X16 >>= 1; + mask8X8 >>= 1; + mask4X4 >>= 1; + mask4X4Int >>= 1; + } + } + + private static readonly byte[] _num4X4BlocksWideLookup = [1, 1, 2, 2, 2, 4, 4, 4, 8, 8, 8, 16, 16]; + private static readonly byte[] _num4X4BlocksHighLookup = [1, 2, 1, 2, 4, 2, 4, 8, 4, 8, 16, 8, 16]; + private static readonly byte[] _num8X8BlocksWideLookup = [1, 1, 1, 1, 1, 2, 2, 2, 4, 4, 4, 8, 8]; + private static readonly byte[] _num8X8BlocksHighLookup = [1, 1, 1, 1, 2, 1, 2, 4, 2, 4, 8, 4, 8]; + + private static void FilterBlockPlaneNon420( + ref Vp9Common cm, + ref MacroBlockDPlane plane, + ArrayPtr> mi8X8, + int miRow, + int miCol) + { + int ssX = plane.SubsamplingX; + int ssY = plane.SubsamplingY; + int rowStep = 1 << ssY; + int colStep = 1 << ssX; + int rowStepStride = cm.MiStride * rowStep; + ref Buf2D dst = ref plane.Dst; + ArrayPtr dst0 = dst.Buf; + Span mask16X16 = stackalloc int[Constants.MiBlockSize]; + Span mask8X8 = stackalloc int[Constants.MiBlockSize]; + Span mask4X4 = stackalloc int[Constants.MiBlockSize]; + Span mask4X4Int = stackalloc int[Constants.MiBlockSize]; + Span lfl = stackalloc byte[Constants.MiBlockSize * Constants.MiBlockSize]; + + + for (int r = 0; r < Constants.MiBlockSize && miRow + r < cm.MiRows; r += rowStep) + { + uint mask16X16C = 0; + uint mask8X8C = 0; + uint mask4X4C = 0; + uint borderMask; + + // Determine the vertical edges that need filtering + for (int c = 0; c < Constants.MiBlockSize && miCol + c < cm.MiCols; c += colStep) + { + ref ModeInfo mi = ref mi8X8[c].Value; + BlockSize sbType = mi.SbType; + bool skipThis = mi.Skip != 0 && mi.IsInterBlock(); + // left edge of current unit is block/partition edge -> no skip + bool blockEdgeLeft = _num4X4BlocksWideLookup[(int)sbType] <= 1 || (c & (_num8X8BlocksWideLookup[(int)sbType] - 1)) == 0; + bool skipThisC = skipThis && !blockEdgeLeft; + // top edge of current unit is block/partition edge -> no skip + bool blockEdgeAbove = _num4X4BlocksHighLookup[(int)sbType] <= 1 || (r & (_num8X8BlocksHighLookup[(int)sbType] - 1)) == 0; + bool skipThisR = skipThis && !blockEdgeAbove; + TxSize txSize = mi.GetUvTxSize(ref plane); + bool skipBorder4X4C = ssX != 0 && miCol + c == cm.MiCols - 1; + bool skipBorder4X4R = ssY != 0 && miRow + r == cm.MiRows - 1; + + // Filter level can vary per MI + if ((lfl[(r << 3) + (c >> ssX)] = GetFilterLevel(ref cm.LfInfo, ref mi)) == 0) + { + continue; + } + + // Build masks based on the transform size of each block + if (txSize == TxSize.Tx32X32) + { + if (!skipThisC && ((c >> ssX) & 3) == 0) + { + if (!skipBorder4X4C) + { + mask16X16C |= 1u << (c >> ssX); + } + else + { + mask8X8C |= 1u << (c >> ssX); + } + } + + if (!skipThisR && ((r >> ssY) & 3) == 0) + { + if (!skipBorder4X4R) + { + mask16X16[r] |= 1 << (c >> ssX); + } + else + { + mask8X8[r] |= 1 << (c >> ssX); + } + } + } + else if (txSize == TxSize.Tx16X16) + { + if (!skipThisC && ((c >> ssX) & 1) == 0) + { + if (!skipBorder4X4C) + { + mask16X16C |= 1u << (c >> ssX); + } + else + { + mask8X8C |= 1u << (c >> ssX); + } + } + + if (!skipThisR && ((r >> ssY) & 1) == 0) + { + if (!skipBorder4X4R) + { + mask16X16[r] |= 1 << (c >> ssX); + } + else + { + mask8X8[r] |= 1 << (c >> ssX); + } + } + } + else + { + // force 8x8 filtering on 32x32 boundaries + if (!skipThisC) + { + if (txSize == TxSize.Tx8X8 || ((c >> ssX) & 3) == 0) + { + mask8X8C |= 1u << (c >> ssX); + } + else + { + mask4X4C |= 1u << (c >> ssX); + } + } + + if (!skipThisR) + { + if (txSize == TxSize.Tx8X8 || ((r >> ssY) & 3) == 0) + { + mask8X8[r] |= 1 << (c >> ssX); + } + else + { + mask4X4[r] |= 1 << (c >> ssX); + } + } + + if (!skipThis && txSize < TxSize.Tx8X8 && !skipBorder4X4C) + { + mask4X4Int[r] |= 1 << (c >> ssX); + } + } + } + + // Disable filtering on the leftmost column + borderMask = ~(miCol == 0 ? 1u : 0u); + + if (cm.UseHighBitDepth) + { + HighbdFilterSelectivelyVert( + ConvertToUshortPtr(dst.Buf), + dst.Stride, + mask16X16C & borderMask, + mask8X8C & borderMask, + mask4X4C & borderMask, + (uint)mask4X4Int[r], + cm.LfInfo.Lfthr.AsSpan(), + lfl.Slice(r << 3), + (int)cm.BitDepth); + } + else + { + FilterSelectivelyVert( + dst.Buf, + dst.Stride, + mask16X16C & borderMask, + mask8X8C & borderMask, + mask4X4C & borderMask, + (uint)mask4X4Int[r], + cm.LfInfo.Lfthr.AsSpan(), + lfl.Slice(r << 3)); + } + + dst.Buf = dst.Buf.Slice(8 * dst.Stride); + mi8X8 = mi8X8.Slice(rowStepStride); + } + + // Now do horizontal pass + dst.Buf = dst0; + for (int r = 0; r < Constants.MiBlockSize && miRow + r < cm.MiRows; r += rowStep) + { + bool skipBorder4X4R = ssY != 0 && miRow + r == cm.MiRows - 1; + uint mask4X4IntR = skipBorder4X4R ? 0u : (uint)mask4X4Int[r]; + + uint mask16X16R; + uint mask8X8R; + uint mask4X4R; + + if (miRow + r == 0) + { + mask16X16R = 0; + mask8X8R = 0; + mask4X4R = 0; + } + else + { + mask16X16R = (uint)mask16X16[r]; + mask8X8R = (uint)mask8X8[r]; + mask4X4R = (uint)mask4X4[r]; + } + + if (cm.UseHighBitDepth) + { + HighbdFilterSelectivelyHoriz( + ConvertToUshortPtr(dst.Buf), + dst.Stride, + mask16X16R, + mask8X8R, + mask4X4R, + mask4X4IntR, + cm.LfInfo.Lfthr.AsSpan(), + lfl.Slice(r << 3), + (int)cm.BitDepth); + } + else + { + FilterSelectivelyHoriz( + dst.Buf, + dst.Stride, + mask16X16R, + mask8X8R, + mask4X4R, + mask4X4IntR, + cm.LfInfo.Lfthr.AsSpan(), + lfl.Slice(r << 3)); + } + + dst.Buf = dst.Buf.Slice(8 * dst.Stride); + } + } + + private static void FilterBlockPlaneSs00(ref Vp9Common cm, ref MacroBlockDPlane plane, int miRow, + ref LoopFilterMask lfm) + { + ref Buf2D dst = ref plane.Dst; + ArrayPtr dst0 = dst.Buf; + ulong mask16X16 = lfm.LeftY[(int)TxSize.Tx16X16]; + ulong mask8X8 = lfm.LeftY[(int)TxSize.Tx8X8]; + ulong mask4X4 = lfm.LeftY[(int)TxSize.Tx4X4]; + ulong mask4X4Int = lfm.Int4X4Y; + + Debug.Assert(plane.SubsamplingX == 0 && plane.SubsamplingY == 0); + + // Vertical pass: do 2 rows at one time + for (int r = 0; r < Constants.MiBlockSize && miRow + r < cm.MiRows; r += 2) + { + if (cm.UseHighBitDepth) + { + // Disable filtering on the leftmost column. + HighbdFilterSelectivelyVertRow2( + plane.SubsamplingX, + ConvertToUshortPtr(dst.Buf), + dst.Stride, + (uint)mask16X16, + (uint)mask8X8, + (uint)mask4X4, + (uint)mask4X4Int, + cm.LfInfo.Lfthr.AsSpan(), + lfm.LflY.AsSpan().Slice(r << 3), + (int)cm.BitDepth); + } + else + { + // Disable filtering on the leftmost column. + FilterSelectivelyVertRow2( + plane.SubsamplingX, + dst.Buf, + dst.Stride, + (uint)mask16X16, + (uint)mask8X8, + (uint)mask4X4, + (uint)mask4X4Int, + cm.LfInfo.Lfthr.AsSpan(), + lfm.LflY.AsSpan().Slice(r << 3)); + } + + dst.Buf = dst.Buf.Slice(16 * dst.Stride); + mask16X16 >>= 16; + mask8X8 >>= 16; + mask4X4 >>= 16; + mask4X4Int >>= 16; + } + + // Horizontal pass + dst.Buf = dst0; + mask16X16 = lfm.AboveY[(int)TxSize.Tx16X16]; + mask8X8 = lfm.AboveY[(int)TxSize.Tx8X8]; + mask4X4 = lfm.AboveY[(int)TxSize.Tx4X4]; + mask4X4Int = lfm.Int4X4Y; + + for (int r = 0; r < Constants.MiBlockSize && miRow + r < cm.MiRows; r++) + { + uint mask16X16R; + uint mask8X8R; + uint mask4X4R; + + if (miRow + r == 0) + { + mask16X16R = 0; + mask8X8R = 0; + mask4X4R = 0; + } + else + { + mask16X16R = (uint)mask16X16 & 0xff; + mask8X8R = (uint)mask8X8 & 0xff; + mask4X4R = (uint)mask4X4 & 0xff; + } + + if (cm.UseHighBitDepth) + { + HighbdFilterSelectivelyHoriz( + ConvertToUshortPtr(dst.Buf), + dst.Stride, + mask16X16R, + mask8X8R, + mask4X4R, + (uint)mask4X4Int & 0xff, + cm.LfInfo.Lfthr.AsSpan(), + lfm.LflY.AsSpan().Slice(r << 3), + (int)cm.BitDepth); + } + else + { + FilterSelectivelyHoriz( + dst.Buf, + dst.Stride, + mask16X16R, + mask8X8R, + mask4X4R, + (uint)mask4X4Int & 0xff, + cm.LfInfo.Lfthr.AsSpan(), + lfm.LflY.AsSpan().Slice(r << 3)); + } + + dst.Buf = dst.Buf.Slice(8 * dst.Stride); + mask16X16 >>= 8; + mask8X8 >>= 8; + mask4X4 >>= 8; + mask4X4Int >>= 8; + } + } + + private static void FilterBlockPlaneSs11(ref Vp9Common cm, ref MacroBlockDPlane plane, int miRow, + ref LoopFilterMask lfm) + { + Buf2D dst = plane.Dst; + ArrayPtr dst0 = dst.Buf; + + Span lflUv = stackalloc byte[16]; + + ushort mask16X16 = lfm.LeftUv[(int)TxSize.Tx16X16]; + ushort mask8X8 = lfm.LeftUv[(int)TxSize.Tx8X8]; + ushort mask4X4 = lfm.LeftUv[(int)TxSize.Tx4X4]; + ushort mask4X4Int = lfm.Int4X4Uv; + + Debug.Assert(plane.SubsamplingX == 1 && plane.SubsamplingY == 1); + + // Vertical pass: do 2 rows at one time + for (int r = 0; r < Constants.MiBlockSize && miRow + r < cm.MiRows; r += 4) + { + for (int c = 0; c < Constants.MiBlockSize >> 1; c++) + { + lflUv[(r << 1) + c] = lfm.LflY[(r << 3) + (c << 1)]; + lflUv[((r + 2) << 1) + c] = lfm.LflY[((r + 2) << 3) + (c << 1)]; + } + + if (cm.UseHighBitDepth) + { + // Disable filtering on the leftmost column. + HighbdFilterSelectivelyVertRow2( + plane.SubsamplingX, + ConvertToUshortPtr(dst.Buf), + dst.Stride, + mask16X16, + mask8X8, + mask4X4, + mask4X4Int, + cm.LfInfo.Lfthr.AsSpan(), + lflUv.Slice(r << 1), + (int)cm.BitDepth); + } + else + { + // Disable filtering on the leftmost column. + FilterSelectivelyVertRow2( + plane.SubsamplingX, + dst.Buf, + dst.Stride, + mask16X16, + mask8X8, + mask4X4, + mask4X4Int, + cm.LfInfo.Lfthr.AsSpan(), + lflUv.Slice(r << 1)); + } + + dst.Buf = dst.Buf.Slice(16 * dst.Stride); + mask16X16 >>= 8; + mask8X8 >>= 8; + mask4X4 >>= 8; + mask4X4Int >>= 8; + } + + // Horizontal pass + dst.Buf = dst0; + mask16X16 = lfm.AboveUv[(int)TxSize.Tx16X16]; + mask8X8 = lfm.AboveUv[(int)TxSize.Tx8X8]; + mask4X4 = lfm.AboveUv[(int)TxSize.Tx4X4]; + mask4X4Int = lfm.Int4X4Uv; + + for (int r = 0; r < Constants.MiBlockSize && miRow + r < cm.MiRows; r += 2) + { + bool skipBorder4X4R = miRow + r == cm.MiRows - 1; + uint mask4X4IntR = skipBorder4X4R ? 0u : (uint)mask4X4Int & 0xf; + uint mask16X16R; + uint mask8X8R; + uint mask4X4R; + + if (miRow + r == 0) + { + mask16X16R = 0; + mask8X8R = 0; + mask4X4R = 0; + } + else + { + mask16X16R = (uint)mask16X16 & 0xf; + mask8X8R = (uint)mask8X8 & 0xf; + mask4X4R = (uint)mask4X4 & 0xf; + } + + if (cm.UseHighBitDepth) + { + HighbdFilterSelectivelyHoriz( + ConvertToUshortPtr(dst.Buf), + dst.Stride, + mask16X16R, + mask8X8R, + mask4X4R, + mask4X4IntR, + cm.LfInfo.Lfthr.AsSpan(), + lflUv.Slice(r << 1), + (int)cm.BitDepth); + } + else + { + FilterSelectivelyHoriz( + dst.Buf, + dst.Stride, + mask16X16R, + mask8X8R, + mask4X4R, + mask4X4IntR, + cm.LfInfo.Lfthr.AsSpan(), + lflUv.Slice(r << 1)); + } + + dst.Buf = dst.Buf.Slice(8 * dst.Stride); + mask16X16 >>= 4; + mask8X8 >>= 4; + mask4X4 >>= 4; + mask4X4Int >>= 4; + } + } + + private enum LfPath + { + LfPathSlow, + LfPath420, + LfPath444 + } + + private static void LoopFilterRows( + ref Surface frameBuffer, + ref Vp9Common cm, + Array3 planes, + int start, + int stop, + int step, + bool yOnly, + LfSync lfSync) + { + int numPlanes = yOnly ? 1 : Constants.MaxMbPlane; + int sbCols = TileInfo.MiColsAlignedToSb(cm.MiCols) >> Constants.MiBlockSizeLog2; + LfPath path; + int miRow, miCol; + + if (yOnly) + { + path = LfPath.LfPath444; + } + else if (planes[1].SubsamplingY == 1 && planes[1].SubsamplingX == 1) + { + path = LfPath.LfPath420; + } + else if (planes[1].SubsamplingY == 0 && planes[1].SubsamplingX == 0) + { + path = LfPath.LfPath444; + } + else + { + path = LfPath.LfPathSlow; + } + + for (miRow = start; miRow < stop; miRow += step) + { + ArrayPtr> mi = cm.MiGridVisible.Slice(miRow * cm.MiStride); + Span lfm = GetLfm(ref cm.Lf, miRow, 0); + + for (miCol = 0; miCol < cm.MiCols; miCol += Constants.MiBlockSize, lfm = lfm.Slice(1)) + { + int r = miRow >> Constants.MiBlockSizeLog2; + int c = miCol >> Constants.MiBlockSizeLog2; + int plane; + + lfSync.SyncRead(r, c); + + ReconInter.SetupDstPlanes(ref planes, ref frameBuffer, miRow, miCol); + + AdjustMask(ref cm, miRow, miCol, ref lfm[0]); + + FilterBlockPlaneSs00(ref cm, ref planes[0], miRow, ref lfm[0]); + for (plane = 1; plane < numPlanes; ++plane) + { + switch (path) + { + case LfPath.LfPath420: + FilterBlockPlaneSs11(ref cm, ref planes[plane], miRow, ref lfm[0]); + break; + case LfPath.LfPath444: + FilterBlockPlaneSs00(ref cm, ref planes[plane], miRow, ref lfm[0]); + break; + case LfPath.LfPathSlow: + FilterBlockPlaneNon420(ref cm, ref planes[plane], mi.Slice(miCol), miRow, + miCol); + break; + } + } + + lfSync.SyncWrite(r, c, sbCols); + } + } + } + + public static void LoopFilterFrame( + ref Surface frame, + ref Vp9Common cm, + ref MacroBlockD xd, + int frameFilterLevel, + bool yOnly, + bool partialFrame) + { + if (frameFilterLevel == 0) + { + return; + } + + int startMiRow = 0; + int miRowsToFilter = cm.MiRows; + + if (partialFrame && cm.MiRows > 8) + { + startMiRow = cm.MiRows >> 1; + startMiRow &= ~7; + miRowsToFilter = Math.Max(cm.MiRows / 8, 8); + } + + int endMiRow = startMiRow + miRowsToFilter; + + LoopFilterRows(ref frame, ref cm, xd.Plane, startMiRow, endMiRow, Constants.MiBlockSize, yOnly, + default); + } + + private static void LoopFilterRowsMt( + ref Surface frameBuffer, + ref Vp9Common cm, + Array3 planes, + int start, + int stop, + bool yOnly, + int threadCount) + { + int sbRows = TileInfo.MiColsAlignedToSb(cm.MiRows) >> Constants.MiBlockSizeLog2; + int numTileCols = 1 << cm.Log2TileCols; + int numWorkers = Math.Min(threadCount, Math.Min(numTileCols, sbRows)); + + LfSync lfSync = new(); + lfSync.Initialize(cm.Width, sbRows); + + Ptr frameBufferPtr = new(ref frameBuffer); + Ptr cmPtr = new(ref cm); + + Parallel.For(0, numWorkers, n => + { + LoopFilterRows( + ref frameBufferPtr.Value, + ref cmPtr.Value, + planes, + start + (n * Constants.MiBlockSize), + stop, + numWorkers * Constants.MiBlockSize, + yOnly, + lfSync); + }); + } + + public static void LoopFilterFrameMt( + ref Surface frame, + ref Vp9Common cm, + ref MacroBlockD xd, + int frameFilterLevel, + bool yOnly, + bool partialFrame, + int threadCount) + { + if (frameFilterLevel == 0) + { + return; + } + + int startMiRow = 0; + int miRowsToFilter = cm.MiRows; + + if (partialFrame && cm.MiRows > 8) + { + startMiRow = cm.MiRows >> 1; + startMiRow &= ~7; + miRowsToFilter = Math.Max(cm.MiRows / 8, 8); + } + + int endMiRow = startMiRow + miRowsToFilter; + + LoopFilterFrameInit(ref cm, frameFilterLevel); + LoopFilterRowsMt(ref frame, ref cm, xd.Plane, startMiRow, endMiRow, yOnly, threadCount); + } + + private static unsafe ArrayPtr ConvertToUshortPtr(ArrayPtr s) + { + return new ArrayPtr((ushort*)s.ToPointer(), s.Length / 2); + } } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Luts.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Luts.cs index 5245b3f32..d54dbeec5 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Luts.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Luts.cs @@ -1,4 +1,4 @@ -using Ryujinx.Common.Memory; +using Ryujinx.Common.Memory; using Ryujinx.Graphics.Nvdec.Vp9.Types; using System; @@ -6,174 +6,182 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { internal static class Luts { - public static ReadOnlySpan SizeGroupLookup => new byte[] { 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3 }; + public static ReadOnlySpan SizeGroupLookup => [0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3]; - public static readonly BlockSize[][] SubsizeLookup = { - new[] - { // PARTITION_NONE - BlockSize.Block4x4, BlockSize.Block4x8, BlockSize.Block8x4, BlockSize.Block8x8, BlockSize.Block8x16, BlockSize.Block16x8, - BlockSize.Block16x16, BlockSize.Block16x32, BlockSize.Block32x16, BlockSize.Block32x32, BlockSize.Block32x64, - BlockSize.Block64x32, BlockSize.Block64x64, - }, - new[] - { // PARTITION_HORZ - BlockSize.BlockInvalid, BlockSize.BlockInvalid, BlockSize.BlockInvalid, BlockSize.Block8x4, BlockSize.BlockInvalid, - BlockSize.BlockInvalid, BlockSize.Block16x8, BlockSize.BlockInvalid, BlockSize.BlockInvalid, BlockSize.Block32x16, - BlockSize.BlockInvalid, BlockSize.BlockInvalid, BlockSize.Block64x32, - }, - new[] - { // PARTITION_VERT - BlockSize.BlockInvalid, BlockSize.BlockInvalid, BlockSize.BlockInvalid, BlockSize.Block4x8, BlockSize.BlockInvalid, - BlockSize.BlockInvalid, BlockSize.Block8x16, BlockSize.BlockInvalid, BlockSize.BlockInvalid, BlockSize.Block16x32, - BlockSize.BlockInvalid, BlockSize.BlockInvalid, BlockSize.Block32x64, - }, - new[] - { // PARTITION_SPLIT - BlockSize.BlockInvalid, BlockSize.BlockInvalid, BlockSize.BlockInvalid, BlockSize.Block4x4, BlockSize.BlockInvalid, - BlockSize.BlockInvalid, BlockSize.Block8x8, BlockSize.BlockInvalid, BlockSize.BlockInvalid, BlockSize.Block16x16, - BlockSize.BlockInvalid, BlockSize.BlockInvalid, BlockSize.Block32x32, - }, + public static readonly BlockSize[][] SubsizeLookup = new BlockSize[][] + { + [ // PARTITION_NONE + BlockSize.Block4X4, BlockSize.Block4X8, BlockSize.Block8X4, BlockSize.Block8X8, BlockSize.Block8X16, BlockSize.Block16X8, + BlockSize.Block16X16, BlockSize.Block16X32, BlockSize.Block32X16, BlockSize.Block32X32, BlockSize.Block32X64, + BlockSize.Block64X32, BlockSize.Block64X64 + ], + [ // PARTITION_HORZ + BlockSize.BlockInvalid, BlockSize.BlockInvalid, BlockSize.BlockInvalid, BlockSize.Block8X4, BlockSize.BlockInvalid, + BlockSize.BlockInvalid, BlockSize.Block16X8, BlockSize.BlockInvalid, BlockSize.BlockInvalid, BlockSize.Block32X16, + BlockSize.BlockInvalid, BlockSize.BlockInvalid, BlockSize.Block64X32 + ], + [ // PARTITION_VERT + BlockSize.BlockInvalid, BlockSize.BlockInvalid, BlockSize.BlockInvalid, BlockSize.Block4X8, BlockSize.BlockInvalid, + BlockSize.BlockInvalid, BlockSize.Block8X16, BlockSize.BlockInvalid, BlockSize.BlockInvalid, BlockSize.Block16X32, + BlockSize.BlockInvalid, BlockSize.BlockInvalid, BlockSize.Block32X64 + ], + [ // PARTITION_SPLIT + BlockSize.BlockInvalid, BlockSize.BlockInvalid, BlockSize.BlockInvalid, BlockSize.Block4X4, BlockSize.BlockInvalid, + BlockSize.BlockInvalid, BlockSize.Block8X8, BlockSize.BlockInvalid, BlockSize.BlockInvalid, BlockSize.Block16X16, + BlockSize.BlockInvalid, BlockSize.BlockInvalid, BlockSize.Block32X32 + ] }; - public static readonly TxSize[] MaxTxSizeLookup = { - TxSize.Tx4x4, TxSize.Tx4x4, TxSize.Tx4x4, TxSize.Tx8x8, TxSize.Tx8x8, TxSize.Tx8x8, TxSize.Tx16x16, - TxSize.Tx16x16, TxSize.Tx16x16, TxSize.Tx32x32, TxSize.Tx32x32, TxSize.Tx32x32, TxSize.Tx32x32, - }; + public static readonly TxSize[] MaxTxSizeLookup = + [ + TxSize.Tx4X4, TxSize.Tx4X4, TxSize.Tx4X4, TxSize.Tx8X8, TxSize.Tx8X8, TxSize.Tx8X8, TxSize.Tx16X16, + TxSize.Tx16X16, TxSize.Tx16X16, TxSize.Tx32X32, TxSize.Tx32X32, TxSize.Tx32X32, TxSize.Tx32X32 + ]; - public static readonly TxSize[] TxModeToBiggestTxSize = { - TxSize.Tx4x4, // ONLY_4X4 - TxSize.Tx8x8, // ALLOW_8X8 - TxSize.Tx16x16, // ALLOW_16X16 - TxSize.Tx32x32, // ALLOW_32X32 - TxSize.Tx32x32, // TX_MODE_SELECT - }; + public static readonly TxSize[] TxModeToBiggestTxSize = + [ + TxSize.Tx4X4, // ONLY_4X4 + TxSize.Tx8X8, // ALLOW_8X8 + TxSize.Tx16X16, // ALLOW_16X16 + TxSize.Tx32X32, // ALLOW_32X32 + TxSize.Tx32X32 // TX_MODE_SELECT + ]; - public static readonly BlockSize[][][] SsSizeLookup = { + public static readonly BlockSize[][][] SsSizeLookup = + [ // ss_x == 0 ss_x == 0 ss_x == 1 ss_x == 1 // ss_y == 0 ss_y == 1 ss_y == 0 ss_y == 1 - new[] { new[] { BlockSize.Block4x4, BlockSize.BlockInvalid }, new[] { BlockSize.BlockInvalid, BlockSize.BlockInvalid } }, - new[] { new[] { BlockSize.Block4x8, BlockSize.Block4x4 }, new[] { BlockSize.BlockInvalid, BlockSize.BlockInvalid } }, - new[] { new[] { BlockSize.Block8x4, BlockSize.BlockInvalid }, new[] { BlockSize.Block4x4, BlockSize.BlockInvalid } }, - new[] { new[] { BlockSize.Block8x8, BlockSize.Block8x4 }, new[] { BlockSize.Block4x8, BlockSize.Block4x4 } }, - new[] { new[] { BlockSize.Block8x16, BlockSize.Block8x8 }, new[] { BlockSize.BlockInvalid, BlockSize.Block4x8 } }, - new[] { new[] { BlockSize.Block16x8, BlockSize.BlockInvalid }, new[] { BlockSize.Block8x8, BlockSize.Block8x4 } }, - new[] { new[] { BlockSize.Block16x16, BlockSize.Block16x8 }, new[] { BlockSize.Block8x16, BlockSize.Block8x8 } }, - new[] { new[] { BlockSize.Block16x32, BlockSize.Block16x16 }, new[] { BlockSize.BlockInvalid, BlockSize.Block8x16 } }, - new[] { new[] { BlockSize.Block32x16, BlockSize.BlockInvalid }, new[] { BlockSize.Block16x16, BlockSize.Block16x8 } }, - new[] { new[] { BlockSize.Block32x32, BlockSize.Block32x16 }, new[] { BlockSize.Block16x32, BlockSize.Block16x16 } }, - new[] { new[] { BlockSize.Block32x64, BlockSize.Block32x32 }, new[] { BlockSize.BlockInvalid, BlockSize.Block16x32 } }, - new[] { new[] { BlockSize.Block64x32, BlockSize.BlockInvalid }, new[] { BlockSize.Block32x32, BlockSize.Block32x16 } }, - new[] { new[] { BlockSize.Block64x64, BlockSize.Block64x32 }, new[] { BlockSize.Block32x64, BlockSize.Block32x32 } }, - }; + new BlockSize[][] { [BlockSize.Block4X4, BlockSize.BlockInvalid], [BlockSize.BlockInvalid, BlockSize.BlockInvalid + ] + }, + new BlockSize[][] { [BlockSize.Block4X8, BlockSize.Block4X4], [BlockSize.BlockInvalid, BlockSize.BlockInvalid + ] + }, + new BlockSize[][] { [BlockSize.Block8X4, BlockSize.BlockInvalid], [BlockSize.Block4X4, BlockSize.BlockInvalid + ] + }, + new BlockSize[][] { [BlockSize.Block8X8, BlockSize.Block8X4], [BlockSize.Block4X8, BlockSize.Block4X4] }, + new BlockSize[][] { [BlockSize.Block8X16, BlockSize.Block8X8], [BlockSize.BlockInvalid, BlockSize.Block4X8] + }, + new BlockSize[][] { [BlockSize.Block16X8, BlockSize.BlockInvalid], [BlockSize.Block8X8, BlockSize.Block8X4] + }, + new BlockSize[][] { [BlockSize.Block16X16, BlockSize.Block16X8], [BlockSize.Block8X16, BlockSize.Block8X8] }, + new BlockSize[][] { [BlockSize.Block16X32, BlockSize.Block16X16], [BlockSize.BlockInvalid, BlockSize.Block8X16 + ] + }, + new BlockSize[][] { [BlockSize.Block32X16, BlockSize.BlockInvalid], [BlockSize.Block16X16, BlockSize.Block16X8 + ] + }, + new BlockSize[][] { [BlockSize.Block32X32, BlockSize.Block32X16], [BlockSize.Block16X32, BlockSize.Block16X16 + ] + }, + new BlockSize[][] { [BlockSize.Block32X64, BlockSize.Block32X32], [BlockSize.BlockInvalid, BlockSize.Block16X32 + ] + }, + new BlockSize[][] { [BlockSize.Block64X32, BlockSize.BlockInvalid], [BlockSize.Block32X32, BlockSize.Block32X16 + ] + }, + new BlockSize[][] { [BlockSize.Block64X64, BlockSize.Block64X32], [BlockSize.Block32X64, BlockSize.Block32X32 + ] + } + ]; - public static readonly TxSize[][][][] UvTxsizeLookup = { - // ss_x == 0 ss_x == 0 ss_x == 1 ss_x == 1 + public static readonly TxSize[][][][] UvTxsizeLookup = + [ + // ss_x == 0 ss_x == 0 ss_x == 1 ss_x == 1 // ss_y == 0 ss_y == 1 ss_y == 0 ss_y == 1 - new[] - { - // BLOCK_4X4 - new[] { new[] { TxSize.Tx4x4, TxSize.Tx4x4 }, new[] { TxSize.Tx4x4, TxSize.Tx4x4 } }, - new[] { new[] { TxSize.Tx4x4, TxSize.Tx4x4 }, new[] { TxSize.Tx4x4, TxSize.Tx4x4 } }, - new[] { new[] { TxSize.Tx4x4, TxSize.Tx4x4 }, new[] { TxSize.Tx4x4, TxSize.Tx4x4 } }, - new[] { new[] { TxSize.Tx4x4, TxSize.Tx4x4 }, new[] { TxSize.Tx4x4, TxSize.Tx4x4 } }, - }, - new[] - { - // BLOCK_4X8 - new[] { new[] { TxSize.Tx4x4, TxSize.Tx4x4 }, new[] { TxSize.Tx4x4, TxSize.Tx4x4 } }, - new[] { new[] { TxSize.Tx4x4, TxSize.Tx4x4 }, new[] { TxSize.Tx4x4, TxSize.Tx4x4 } }, - new[] { new[] { TxSize.Tx4x4, TxSize.Tx4x4 }, new[] { TxSize.Tx4x4, TxSize.Tx4x4 } }, - new[] { new[] { TxSize.Tx4x4, TxSize.Tx4x4 }, new[] { TxSize.Tx4x4, TxSize.Tx4x4 } }, - }, - new[] - { - // BLOCK_8X4 - new[] { new[] { TxSize.Tx4x4, TxSize.Tx4x4 }, new[] { TxSize.Tx4x4, TxSize.Tx4x4 } }, - new[] { new[] { TxSize.Tx4x4, TxSize.Tx4x4 }, new[] { TxSize.Tx4x4, TxSize.Tx4x4 } }, - new[] { new[] { TxSize.Tx4x4, TxSize.Tx4x4 }, new[] { TxSize.Tx4x4, TxSize.Tx4x4 } }, - new[] { new[] { TxSize.Tx4x4, TxSize.Tx4x4 }, new[] { TxSize.Tx4x4, TxSize.Tx4x4 } }, - }, - new[] - { - // BLOCK_8X8 - new[] { new[] { TxSize.Tx4x4, TxSize.Tx4x4 }, new[] { TxSize.Tx4x4, TxSize.Tx4x4 } }, - new[] { new[] { TxSize.Tx8x8, TxSize.Tx4x4 }, new[] { TxSize.Tx4x4, TxSize.Tx4x4 } }, - new[] { new[] { TxSize.Tx8x8, TxSize.Tx4x4 }, new[] { TxSize.Tx4x4, TxSize.Tx4x4 } }, - new[] { new[] { TxSize.Tx8x8, TxSize.Tx4x4 }, new[] { TxSize.Tx4x4, TxSize.Tx4x4 } }, - }, - new[] - { - // BLOCK_8X16 - new[] { new[] { TxSize.Tx4x4, TxSize.Tx4x4 }, new[] { TxSize.Tx4x4, TxSize.Tx4x4 } }, - new[] { new[] { TxSize.Tx8x8, TxSize.Tx8x8 }, new[] { TxSize.Tx4x4, TxSize.Tx4x4 } }, - new[] { new[] { TxSize.Tx8x8, TxSize.Tx8x8 }, new[] { TxSize.Tx4x4, TxSize.Tx4x4 } }, - new[] { new[] { TxSize.Tx8x8, TxSize.Tx8x8 }, new[] { TxSize.Tx4x4, TxSize.Tx4x4 } }, - }, - new[] - { - // BLOCK_16X8 - new[] { new[] { TxSize.Tx4x4, TxSize.Tx4x4 }, new[] { TxSize.Tx4x4, TxSize.Tx4x4 } }, - new[] { new[] { TxSize.Tx8x8, TxSize.Tx4x4 }, new[] { TxSize.Tx8x8, TxSize.Tx4x4 } }, - new[] { new[] { TxSize.Tx8x8, TxSize.Tx4x4 }, new[] { TxSize.Tx8x8, TxSize.Tx8x8 } }, - new[] { new[] { TxSize.Tx8x8, TxSize.Tx4x4 }, new[] { TxSize.Tx8x8, TxSize.Tx8x8 } }, - }, - new[] - { - // BLOCK_16X16 - new[] { new[] { TxSize.Tx4x4, TxSize.Tx4x4 }, new[] { TxSize.Tx4x4, TxSize.Tx4x4 } }, - new[] { new[] { TxSize.Tx8x8, TxSize.Tx8x8 }, new[] { TxSize.Tx8x8, TxSize.Tx8x8 } }, - new[] { new[] { TxSize.Tx16x16, TxSize.Tx8x8 }, new[] { TxSize.Tx8x8, TxSize.Tx8x8 } }, - new[] { new[] { TxSize.Tx16x16, TxSize.Tx8x8 }, new[] { TxSize.Tx8x8, TxSize.Tx8x8 } }, - }, - new[] - { - // BLOCK_16X32 - new[] { new[] { TxSize.Tx4x4, TxSize.Tx4x4 }, new[] { TxSize.Tx4x4, TxSize.Tx4x4 } }, - new[] { new[] { TxSize.Tx8x8, TxSize.Tx8x8 }, new[] { TxSize.Tx8x8, TxSize.Tx8x8 } }, - new[] { new[] { TxSize.Tx16x16, TxSize.Tx16x16 }, new[] { TxSize.Tx8x8, TxSize.Tx8x8 } }, - new[] { new[] { TxSize.Tx16x16, TxSize.Tx16x16 }, new[] { TxSize.Tx8x8, TxSize.Tx8x8 } }, - }, - new[] - { - // BLOCK_32X16 - new[] { new[] { TxSize.Tx4x4, TxSize.Tx4x4 }, new[] { TxSize.Tx4x4, TxSize.Tx4x4 } }, - new[] { new[] { TxSize.Tx8x8, TxSize.Tx8x8 }, new[] { TxSize.Tx8x8, TxSize.Tx8x8 } }, - new[] { new[] { TxSize.Tx16x16, TxSize.Tx8x8 }, new[] { TxSize.Tx16x16, TxSize.Tx8x8 } }, - new[] { new[] { TxSize.Tx16x16, TxSize.Tx8x8 }, new[] { TxSize.Tx16x16, TxSize.Tx8x8 } }, - }, - new[] - { - // BLOCK_32X32 - new[] { new[] { TxSize.Tx4x4, TxSize.Tx4x4 }, new[] { TxSize.Tx4x4, TxSize.Tx4x4 } }, - new[] { new[] { TxSize.Tx8x8, TxSize.Tx8x8 }, new[] { TxSize.Tx8x8, TxSize.Tx8x8 } }, - new[] { new[] { TxSize.Tx16x16, TxSize.Tx16x16 }, new[] { TxSize.Tx16x16, TxSize.Tx16x16 } }, - new[] { new[] { TxSize.Tx32x32, TxSize.Tx16x16 }, new[] { TxSize.Tx16x16, TxSize.Tx16x16 } }, - }, - new[] - { - // BLOCK_32X64 - new[] { new[] { TxSize.Tx4x4, TxSize.Tx4x4 }, new[] { TxSize.Tx4x4, TxSize.Tx4x4 } }, - new[] { new[] { TxSize.Tx8x8, TxSize.Tx8x8 }, new[] { TxSize.Tx8x8, TxSize.Tx8x8 } }, - new[] { new[] { TxSize.Tx16x16, TxSize.Tx16x16 }, new[] { TxSize.Tx16x16, TxSize.Tx16x16 } }, - new[] { new[] { TxSize.Tx32x32, TxSize.Tx32x32 }, new[] { TxSize.Tx16x16, TxSize.Tx16x16 } }, - }, - new[] - { - // BLOCK_64X32 - new[] { new[] { TxSize.Tx4x4, TxSize.Tx4x4 }, new[] { TxSize.Tx4x4, TxSize.Tx4x4 } }, - new[] { new[] { TxSize.Tx8x8, TxSize.Tx8x8 }, new[] { TxSize.Tx8x8, TxSize.Tx8x8 } }, - new[] { new[] { TxSize.Tx16x16, TxSize.Tx16x16 }, new[] { TxSize.Tx16x16, TxSize.Tx16x16 } }, - new[] { new[] { TxSize.Tx32x32, TxSize.Tx16x16 }, new[] { TxSize.Tx32x32, TxSize.Tx16x16 } }, - }, - new[] - { - // BLOCK_64X64 - new[] { new[] { TxSize.Tx4x4, TxSize.Tx4x4 }, new[] { TxSize.Tx4x4, TxSize.Tx4x4 } }, - new[] { new[] { TxSize.Tx8x8, TxSize.Tx8x8 }, new[] { TxSize.Tx8x8, TxSize.Tx8x8 } }, - new[] { new[] { TxSize.Tx16x16, TxSize.Tx16x16 }, new[] { TxSize.Tx16x16, TxSize.Tx16x16 } }, - new[] { new[] { TxSize.Tx32x32, TxSize.Tx32x32 }, new[] { TxSize.Tx32x32, TxSize.Tx32x32 } }, - }, - }; + [ + // BLOCK_4X4 + new TxSize[][] { [TxSize.Tx4X4, TxSize.Tx4X4], [TxSize.Tx4X4, TxSize.Tx4X4] }, + new TxSize[][] { [TxSize.Tx4X4, TxSize.Tx4X4], [TxSize.Tx4X4, TxSize.Tx4X4] }, + new TxSize[][] { [TxSize.Tx4X4, TxSize.Tx4X4], [TxSize.Tx4X4, TxSize.Tx4X4] }, + new TxSize[][] { [TxSize.Tx4X4, TxSize.Tx4X4], [TxSize.Tx4X4, TxSize.Tx4X4] } + ], + [ + // BLOCK_4x8 + new TxSize[][] { [TxSize.Tx4X4, TxSize.Tx4X4], [TxSize.Tx4X4, TxSize.Tx4X4] }, + new TxSize[][] { [TxSize.Tx4X4, TxSize.Tx4X4], [TxSize.Tx4X4, TxSize.Tx4X4] }, + new TxSize[][] { [TxSize.Tx4X4, TxSize.Tx4X4], [TxSize.Tx4X4, TxSize.Tx4X4] }, + new TxSize[][] { [TxSize.Tx4X4, TxSize.Tx4X4], [TxSize.Tx4X4, TxSize.Tx4X4] } + ], + [ + // BLOCK_8x4 + new TxSize[][] { [TxSize.Tx4X4, TxSize.Tx4X4], [TxSize.Tx4X4, TxSize.Tx4X4] }, + new TxSize[][] { [TxSize.Tx4X4, TxSize.Tx4X4], [TxSize.Tx4X4, TxSize.Tx4X4] }, + new TxSize[][] { [TxSize.Tx4X4, TxSize.Tx4X4], [TxSize.Tx4X4, TxSize.Tx4X4] }, + new TxSize[][] { [TxSize.Tx4X4, TxSize.Tx4X4], [TxSize.Tx4X4, TxSize.Tx4X4] } + ], + [ + // BLOCK_8X8 + new TxSize[][] { [TxSize.Tx4X4, TxSize.Tx4X4], [TxSize.Tx4X4, TxSize.Tx4X4] }, + new TxSize[][] { [TxSize.Tx8X8, TxSize.Tx4X4], [TxSize.Tx4X4, TxSize.Tx4X4] }, + new TxSize[][] { [TxSize.Tx8X8, TxSize.Tx4X4], [TxSize.Tx4X4, TxSize.Tx4X4] }, + new TxSize[][] { [TxSize.Tx8X8, TxSize.Tx4X4], [TxSize.Tx4X4, TxSize.Tx4X4] } + ], + [ + // BLOCK_8x16 + new TxSize[][] { [TxSize.Tx4X4, TxSize.Tx4X4], [TxSize.Tx4X4, TxSize.Tx4X4] }, + new TxSize[][] { [TxSize.Tx8X8, TxSize.Tx8X8], [TxSize.Tx4X4, TxSize.Tx4X4] }, + new TxSize[][] { [TxSize.Tx8X8, TxSize.Tx8X8], [TxSize.Tx4X4, TxSize.Tx4X4] }, + new TxSize[][] { [TxSize.Tx8X8, TxSize.Tx8X8], [TxSize.Tx4X4, TxSize.Tx4X4] } + ], + [ + // BLOCK_16x8 + new TxSize[][] { [TxSize.Tx4X4, TxSize.Tx4X4], [TxSize.Tx4X4, TxSize.Tx4X4] }, + new TxSize[][] { [TxSize.Tx8X8, TxSize.Tx4X4], [TxSize.Tx8X8, TxSize.Tx4X4] }, + new TxSize[][] { [TxSize.Tx8X8, TxSize.Tx4X4], [TxSize.Tx8X8, TxSize.Tx8X8] }, + new TxSize[][] { [TxSize.Tx8X8, TxSize.Tx4X4], [TxSize.Tx8X8, TxSize.Tx8X8] } + ], + [ + // BLOCK_16X16 + new TxSize[][] { [TxSize.Tx4X4, TxSize.Tx4X4], [TxSize.Tx4X4, TxSize.Tx4X4] }, + new TxSize[][] { [TxSize.Tx8X8, TxSize.Tx8X8], [TxSize.Tx8X8, TxSize.Tx8X8] }, + new TxSize[][] { [TxSize.Tx16X16, TxSize.Tx8X8], [TxSize.Tx8X8, TxSize.Tx8X8] }, + new TxSize[][] { [TxSize.Tx16X16, TxSize.Tx8X8], [TxSize.Tx8X8, TxSize.Tx8X8] } + ], + [ + // BLOCK_16x32 + new TxSize[][] { [TxSize.Tx4X4, TxSize.Tx4X4], [TxSize.Tx4X4, TxSize.Tx4X4] }, + new TxSize[][] { [TxSize.Tx8X8, TxSize.Tx8X8], [TxSize.Tx8X8, TxSize.Tx8X8] }, + new TxSize[][] { [TxSize.Tx16X16, TxSize.Tx16X16], [TxSize.Tx8X8, TxSize.Tx8X8] }, + new TxSize[][] { [TxSize.Tx16X16, TxSize.Tx16X16], [TxSize.Tx8X8, TxSize.Tx8X8] } + ], + [ + // BLOCK_32x16 + new TxSize[][] { [TxSize.Tx4X4, TxSize.Tx4X4], [TxSize.Tx4X4, TxSize.Tx4X4] }, + new TxSize[][] { [TxSize.Tx8X8, TxSize.Tx8X8], [TxSize.Tx8X8, TxSize.Tx8X8] }, + new TxSize[][] { [TxSize.Tx16X16, TxSize.Tx8X8], [TxSize.Tx16X16, TxSize.Tx8X8] }, + new TxSize[][] { [TxSize.Tx16X16, TxSize.Tx8X8], [TxSize.Tx16X16, TxSize.Tx8X8] } + ], + [ + // BLOCK_32X32 + new TxSize[][] { [TxSize.Tx4X4, TxSize.Tx4X4], [TxSize.Tx4X4, TxSize.Tx4X4] }, + new TxSize[][] { [TxSize.Tx8X8, TxSize.Tx8X8], [TxSize.Tx8X8, TxSize.Tx8X8] }, + new TxSize[][] { [TxSize.Tx16X16, TxSize.Tx16X16], [TxSize.Tx16X16, TxSize.Tx16X16] }, + new TxSize[][] { [TxSize.Tx32X32, TxSize.Tx16X16], [TxSize.Tx16X16, TxSize.Tx16X16] } + ], + [ + // BLOCK_32x64 + new TxSize[][] { [TxSize.Tx4X4, TxSize.Tx4X4], [TxSize.Tx4X4, TxSize.Tx4X4] }, + new TxSize[][] { [TxSize.Tx8X8, TxSize.Tx8X8], [TxSize.Tx8X8, TxSize.Tx8X8] }, + new TxSize[][] { [TxSize.Tx16X16, TxSize.Tx16X16], [TxSize.Tx16X16, TxSize.Tx16X16] }, + new TxSize[][] { [TxSize.Tx32X32, TxSize.Tx32X32], [TxSize.Tx16X16, TxSize.Tx16X16] } + ], + [ + // BLOCK_64x32 + new TxSize[][] { [TxSize.Tx4X4, TxSize.Tx4X4], [TxSize.Tx4X4, TxSize.Tx4X4] }, + new TxSize[][] { [TxSize.Tx8X8, TxSize.Tx8X8], [TxSize.Tx8X8, TxSize.Tx8X8] }, + new TxSize[][] { [TxSize.Tx16X16, TxSize.Tx16X16], [TxSize.Tx16X16, TxSize.Tx16X16] }, + new TxSize[][] { [TxSize.Tx32X32, TxSize.Tx16X16], [TxSize.Tx32X32, TxSize.Tx16X16] } + ], + [ + // BLOCK_64x64 + new TxSize[][] { [TxSize.Tx4X4, TxSize.Tx4X4], [TxSize.Tx4X4, TxSize.Tx4X4] }, + new TxSize[][] { [TxSize.Tx8X8, TxSize.Tx8X8], [TxSize.Tx8X8, TxSize.Tx8X8] }, + new TxSize[][] { [TxSize.Tx16X16, TxSize.Tx16X16], [TxSize.Tx16X16, TxSize.Tx16X16] }, + new TxSize[][] { [TxSize.Tx32X32, TxSize.Tx32X32], [TxSize.Tx32X32, TxSize.Tx32X32] } + ] + ]; public struct PartitionContextPair { @@ -190,25 +198,27 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 // Generates 4 bit field in which each bit set to 1 represents // a blocksize partition 1111 means we split 64x64, 32x32, 16x16 // and 8x8. 1000 means we just split the 64x64 to 32x32 - public static readonly PartitionContextPair[] PartitionContextLookup = { - new(15, 15), // 4X4 - {0b1111, 0b1111} - new(15, 14), // 4X8 - {0b1111, 0b1110} - new(14, 15), // 8X4 - {0b1110, 0b1111} - new(14, 14), // 8X8 - {0b1110, 0b1110} - new(14, 12), // 8X16 - {0b1110, 0b1100} - new(12, 14), // 16X8 - {0b1100, 0b1110} - new(12, 12), // 16X16 - {0b1100, 0b1100} - new(12, 8), // 16X32 - {0b1100, 0b1000} - new(8, 12), // 32X16 - {0b1000, 0b1100} - new(8, 8), // 32X32 - {0b1000, 0b1000} - new(8, 0), // 32X64 - {0b1000, 0b0000} - new(0, 8), // 64X32 - {0b0000, 0b1000} - new(0, 0), // 64X64 - {0b0000, 0b0000} - }; + public static readonly PartitionContextPair[] PartitionContextLookup = + [ + new(15, 15), // 4X4 - {0b1111, 0b1111} + new(15, 14), // 4x8 - {0b1111, 0b1110} + new(14, 15), // 8x4 - {0b1110, 0b1111} + new(14, 14), // 8X8 - {0b1110, 0b1110} + new(14, 12), // 8x16 - {0b1110, 0b1100} + new(12, 14), // 16x8 - {0b1100, 0b1110} + new(12, 12), // 16X16 - {0b1100, 0b1100} + new(12, 8), // 16x32 - {0b1100, 0b1000} + new(8, 12), // 32x16 - {0b1000, 0b1100} + new(8, 8), // 32X32 - {0b1000, 0b1000} + new(8, 0), // 32x64 - {0b1000, 0b0000} + new(0, 8), // 64x32 - {0b0000, 0b1000} + new(0, 0) // 64x64 - {0b0000, 0b0000} + ]; // Filter - private static readonly Array8[] _bilinearFilters = { + private static readonly Array8[] _bilinearFilters = + [ NewArray8Short(0, 0, 0, 128, 0, 0, 0, 0), NewArray8Short(0, 0, 0, 120, 8, 0, 0, 0), NewArray8Short(0, 0, 0, 112, 16, 0, 0, 0), NewArray8Short(0, 0, 0, 104, 24, 0, 0, 0), NewArray8Short(0, 0, 0, 96, 32, 0, 0, 0), NewArray8Short(0, 0, 0, 88, 40, 0, 0, 0), @@ -216,11 +226,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 NewArray8Short(0, 0, 0, 64, 64, 0, 0, 0), NewArray8Short(0, 0, 0, 56, 72, 0, 0, 0), NewArray8Short(0, 0, 0, 48, 80, 0, 0, 0), NewArray8Short(0, 0, 0, 40, 88, 0, 0, 0), NewArray8Short(0, 0, 0, 32, 96, 0, 0, 0), NewArray8Short(0, 0, 0, 24, 104, 0, 0, 0), - NewArray8Short(0, 0, 0, 16, 112, 0, 0, 0), NewArray8Short(0, 0, 0, 8, 120, 0, 0, 0), - }; + NewArray8Short(0, 0, 0, 16, 112, 0, 0, 0), NewArray8Short(0, 0, 0, 8, 120, 0, 0, 0) + ]; // Lagrangian interpolation filter - private static readonly Array8[] _subPelFilters8 = { + private static readonly Array8[] _subPelFilters8 = + [ NewArray8Short(0, 0, 0, 128, 0, 0, 0, 0), NewArray8Short(0, 1, -5, 126, 8, -3, 1, 0), NewArray8Short(-1, 3, -10, 122, 18, -6, 2, 0), NewArray8Short(-1, 4, -13, 118, 27, -9, 3, -1), NewArray8Short(-1, 4, -16, 112, 37, -11, 4, -1), NewArray8Short(-1, 5, -18, 105, 48, -14, 4, -1), @@ -228,11 +239,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 NewArray8Short(-1, 6, -19, 78, 78, -19, 6, -1), NewArray8Short(-1, 5, -18, 68, 88, -19, 6, -1), NewArray8Short(-1, 5, -16, 58, 97, -19, 5, -1), NewArray8Short(-1, 4, -14, 48, 105, -18, 5, -1), NewArray8Short(-1, 4, -11, 37, 112, -16, 4, -1), NewArray8Short(-1, 3, -9, 27, 118, -13, 4, -1), - NewArray8Short(0, 2, -6, 18, 122, -10, 3, -1), NewArray8Short(0, 1, -3, 8, 126, -5, 1, 0), - }; + NewArray8Short(0, 2, -6, 18, 122, -10, 3, -1), NewArray8Short(0, 1, -3, 8, 126, -5, 1, 0) + ]; // DCT based filter - private static readonly Array8[] _subPelFilters8S = { + private static readonly Array8[] _subPelFilters8S = + [ NewArray8Short(0, 0, 0, 128, 0, 0, 0, 0), NewArray8Short(-1, 3, -7, 127, 8, -3, 1, 0), NewArray8Short(-2, 5, -13, 125, 17, -6, 3, -1), NewArray8Short(-3, 7, -17, 121, 27, -10, 5, -2), NewArray8Short(-4, 9, -20, 115, 37, -13, 6, -2), NewArray8Short(-4, 10, -23, 108, 48, -16, 8, -3), @@ -240,11 +252,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 NewArray8Short(-4, 11, -23, 80, 80, -23, 11, -4), NewArray8Short(-4, 10, -21, 70, 90, -24, 11, -4), NewArray8Short(-3, 9, -19, 59, 100, -24, 10, -4), NewArray8Short(-3, 8, -16, 48, 108, -23, 10, -4), NewArray8Short(-2, 6, -13, 37, 115, -20, 9, -4), NewArray8Short(-2, 5, -10, 27, 121, -17, 7, -3), - NewArray8Short(-1, 3, -6, 17, 125, -13, 5, -2), NewArray8Short(0, 1, -3, 8, 127, -7, 3, -1), - }; + NewArray8Short(-1, 3, -6, 17, 125, -13, 5, -2), NewArray8Short(0, 1, -3, 8, 127, -7, 3, -1) + ]; // freqmultiplier = 0.5 - private static readonly Array8[] _subPelFilters8Lp = { + private static readonly Array8[] _subPelFilters8Lp = + [ NewArray8Short(0, 0, 0, 128, 0, 0, 0, 0), NewArray8Short(-3, -1, 32, 64, 38, 1, -3, 0), NewArray8Short(-2, -2, 29, 63, 41, 2, -3, 0), NewArray8Short(-2, -2, 26, 63, 43, 4, -4, 0), NewArray8Short(-2, -3, 24, 62, 46, 5, -4, 0), NewArray8Short(-2, -3, 21, 60, 49, 7, -4, 0), @@ -252,65 +265,74 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 NewArray8Short(-1, -4, 14, 55, 55, 14, -4, -1), NewArray8Short(-1, -4, 12, 53, 57, 16, -4, -1), NewArray8Short(0, -4, 9, 51, 59, 18, -4, -1), NewArray8Short(0, -4, 7, 49, 60, 21, -3, -2), NewArray8Short(0, -4, 5, 46, 62, 24, -3, -2), NewArray8Short(0, -4, 4, 43, 63, 26, -2, -2), - NewArray8Short(0, -3, 2, 41, 63, 29, -2, -2), NewArray8Short(0, -3, 1, 38, 64, 32, -1, -3), - }; + NewArray8Short(0, -3, 2, 41, 63, 29, -2, -2), NewArray8Short(0, -3, 1, 38, 64, 32, -1, -3) + ]; private static Array8 NewArray8Short(short e0, short e1, short e2, short e3, short e4, short e5, short e6, short e7) { - Array8 output = new(); - - output[0] = e0; - output[1] = e1; - output[2] = e2; - output[3] = e3; - output[4] = e4; - output[5] = e5; - output[6] = e6; - output[7] = e7; + Array8 output = new() + { + [0] = e0, + [1] = e1, + [2] = e2, + [3] = e3, + [4] = e4, + [5] = e5, + [6] = e6, + [7] = e7 + }; return output; } - public static readonly Array8[][] Vp9FilterKernels = { - _subPelFilters8, _subPelFilters8Lp, _subPelFilters8S, _bilinearFilters, - }; + public static readonly Array8[][] FilterKernels = + [ + _subPelFilters8, _subPelFilters8Lp, _subPelFilters8S, _bilinearFilters + ]; // Scan - private static readonly short[] _defaultScan4X4 = { - 0, 4, 1, 5, 8, 2, 12, 9, 3, 6, 13, 10, 7, 14, 11, 15, - }; + private static readonly short[] _defaultScan4X4 = + [ + 0, 4, 1, 5, 8, 2, 12, 9, 3, 6, 13, 10, 7, 14, 11, 15 + ]; - private static readonly short[] _colScan4X4 = { - 0, 4, 8, 1, 12, 5, 9, 2, 13, 6, 10, 3, 7, 14, 11, 15, - }; + private static readonly short[] _colScan4X4 = + [ + 0, 4, 8, 1, 12, 5, 9, 2, 13, 6, 10, 3, 7, 14, 11, 15 + ]; - private static readonly short[] _rowScan4X4 = { - 0, 1, 4, 2, 5, 3, 6, 8, 9, 7, 12, 10, 13, 11, 14, 15, - }; + private static readonly short[] _rowScan4X4 = + [ + 0, 1, 4, 2, 5, 3, 6, 8, 9, 7, 12, 10, 13, 11, 14, 15 + ]; - private static readonly short[] _defaultScan8X8 = { + private static readonly short[] _defaultScan8X8 = + [ 0, 8, 1, 16, 9, 2, 17, 24, 10, 3, 18, 25, 32, 11, 4, 26, 33, 19, 40, 12, 34, 27, 5, 41, 20, 48, 13, 35, 42, 28, 21, 6, 49, 56, 36, 43, 29, 7, 14, 50, 57, 44, 22, 37, 15, 51, 58, 30, - 45, 23, 52, 59, 38, 31, 60, 53, 46, 39, 61, 54, 47, 62, 55, 63, - }; + 45, 23, 52, 59, 38, 31, 60, 53, 46, 39, 61, 54, 47, 62, 55, 63 + ]; - private static readonly short[] _colScan8X8 = { + private static readonly short[] _colScan8X8 = + [ 0, 8, 16, 1, 24, 9, 32, 17, 2, 40, 25, 10, 33, 18, 48, 3, 26, 41, 11, 56, 19, 34, 4, 49, 27, 42, 12, 35, 20, 57, 50, 28, 5, 43, 13, 36, 58, 51, 21, 44, 6, 29, 59, 37, 14, 52, 22, 7, - 45, 60, 30, 15, 38, 53, 23, 46, 31, 61, 39, 54, 47, 62, 55, 63, - }; + 45, 60, 30, 15, 38, 53, 23, 46, 31, 61, 39, 54, 47, 62, 55, 63 + ]; - private static readonly short[] _rowScan8X8 = { + private static readonly short[] _rowScan8X8 = + [ 0, 1, 2, 8, 9, 3, 16, 10, 4, 17, 11, 24, 5, 18, 25, 12, 19, 26, 32, 6, 13, 20, 33, 27, 7, 34, 40, 21, 28, 41, 14, 35, 48, 42, 29, 36, 49, 22, 43, 15, 56, 37, 50, 44, 30, 57, 23, 51, - 58, 45, 38, 52, 31, 59, 53, 46, 60, 39, 61, 47, 54, 55, 62, 63, - }; + 58, 45, 38, 52, 31, 59, 53, 46, 60, 39, 61, 47, 54, 55, 62, 63 + ]; - private static readonly short[] _defaultScan16X16 = { + private static readonly short[] _defaultScan16X16 = + [ 0, 16, 1, 32, 17, 2, 48, 33, 18, 3, 64, 34, 49, 19, 65, 80, 50, 4, 35, 66, 20, 81, 96, 51, 5, 36, 82, 97, 67, 112, 21, 52, 98, 37, 83, 113, 6, 68, 128, 53, 22, 99, 114, 84, 7, @@ -328,10 +350,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 172, 110, 247, 157, 79, 218, 203, 126, 233, 188, 248, 95, 173, 142, 219, 111, 249, 234, 158, 127, 189, 204, 250, 235, 143, 174, 220, 205, 159, 251, 190, 221, 175, 236, 237, 191, 206, 252, 222, 253, 207, 238, 223, 254, 239, - 255, - }; + 255 + ]; - private static readonly short[] _colScan16X16 = { + private static readonly short[] _colScan16X16 = + [ 0, 16, 32, 48, 1, 64, 17, 80, 33, 96, 49, 2, 65, 112, 18, 81, 34, 128, 50, 97, 3, 66, 144, 19, 113, 35, 82, 160, 98, 51, 129, 4, 67, 176, 20, 114, 145, 83, 36, 99, 130, 52, 192, 5, 161, @@ -349,10 +372,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 78, 203, 31, 141, 234, 94, 47, 188, 63, 157, 110, 250, 219, 79, 126, 204, 173, 142, 95, 189, 111, 235, 158, 220, 251, 127, 174, 143, 205, 236, 159, 190, 221, 252, 175, 206, 237, 191, 253, 222, 238, 207, 254, 223, 239, - 255, - }; + 255 + ]; - private static readonly short[] _rowScan16X16 = { + private static readonly short[] _rowScan16X16 = + [ 0, 1, 2, 16, 3, 17, 4, 18, 32, 5, 33, 19, 6, 34, 48, 20, 49, 7, 35, 21, 50, 64, 8, 36, 65, 22, 51, 37, 80, 9, 66, 52, 23, 38, 81, 67, 10, 53, 24, 82, 68, 96, 39, 11, 54, @@ -370,10 +394,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 217, 231, 95, 246, 232, 126, 203, 247, 233, 173, 218, 142, 111, 158, 188, 248, 127, 234, 219, 249, 189, 204, 143, 174, 159, 250, 235, 205, 220, 175, 190, 251, 221, 191, 206, 236, 207, 237, 252, 222, 253, 223, 238, 239, 254, - 255, - }; + 255 + ]; - private static readonly short[] _defaultScan32X32 = { + private static readonly short[] _defaultScan32X32 = + [ 0, 32, 1, 64, 33, 2, 96, 65, 34, 128, 3, 97, 66, 160, 129, 35, 98, 4, 67, 130, 161, 192, 36, 99, 224, 5, 162, 193, 68, 131, 37, 100, 225, 194, 256, 163, 69, 132, 6, @@ -452,57 +477,64 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 767, 920, 796, 952, 921, 828, 797, 984, 953, 922, 860, 829, 798, 1016, 985, 954, 923, 892, 861, 830, 799, 1017, 986, 955, 893, 862, 831, 1018, 987, 894, 863, 1019, 895, 924, 956, 925, 988, 957, 926, - 1020, 989, 958, 927, 1021, 990, 959, 1022, 991, 1023, - }; + 1020, 989, 958, 927, 1021, 990, 959, 1022, 991, 1023 + ]; // Neighborhood 2-tuples for various scans and blocksizes, // in {top, left} order for each position in corresponding scan order. - private static readonly short[] _defaultScan4X4Neighbors = { + private static readonly short[] _defaultScan4X4Neighbors = + [ 0, 0, 0, 0, 0, 0, 1, 4, 4, 4, 1, 1, 8, 8, 5, 8, 2, - 2, 2, 5, 9, 12, 6, 9, 3, 6, 10, 13, 7, 10, 11, 14, 0, 0, - }; + 2, 2, 5, 9, 12, 6, 9, 3, 6, 10, 13, 7, 10, 11, 14, 0, 0 + ]; - private static readonly short[] _colScan4X4Neighbors = { + private static readonly short[] _colScan4X4Neighbors = + [ 0, 0, 0, 0, 4, 4, 0, 0, 8, 8, 1, 1, 5, 5, 1, 1, 9, - 9, 2, 2, 6, 6, 2, 2, 3, 3, 10, 10, 7, 7, 11, 11, 0, 0, - }; + 9, 2, 2, 6, 6, 2, 2, 3, 3, 10, 10, 7, 7, 11, 11, 0, 0 + ]; - private static readonly short[] _rowScan4X4Neighbors = { + private static readonly short[] _rowScan4X4Neighbors = + [ 0, 0, 0, 0, 0, 0, 1, 1, 4, 4, 2, 2, 5, 5, 4, 4, 8, - 8, 6, 6, 8, 8, 9, 9, 12, 12, 10, 10, 13, 13, 14, 14, 0, 0, - }; + 8, 6, 6, 8, 8, 9, 9, 12, 12, 10, 10, 13, 13, 14, 14, 0, 0 + ]; - private static readonly short[] _colScan8X8Neighbors = { + private static readonly short[] _colScan8X8Neighbors = + [ 0, 0, 0, 0, 8, 8, 0, 0, 16, 16, 1, 1, 24, 24, 9, 9, 1, 1, 32, 32, 17, 17, 2, 2, 25, 25, 10, 10, 40, 40, 2, 2, 18, 18, 33, 33, 3, 3, 48, 48, 11, 11, 26, 26, 3, 3, 41, 41, 19, 19, 34, 34, 4, 4, 27, 27, 12, 12, 49, 49, 42, 42, 20, 20, 4, 4, 35, 35, 5, 5, 28, 28, 50, 50, 43, 43, 13, 13, 36, 36, 5, 5, 21, 21, 51, 51, 29, 29, 6, 6, 44, 44, 14, 14, 6, 6, 37, 37, 52, 52, 22, 22, 7, 7, 30, 30, 45, 45, 15, 15, 38, 38, 23, 23, - 53, 53, 31, 31, 46, 46, 39, 39, 54, 54, 47, 47, 55, 55, 0, 0, - }; + 53, 53, 31, 31, 46, 46, 39, 39, 54, 54, 47, 47, 55, 55, 0, 0 + ]; - private static readonly short[] _rowScan8X8Neighbors = { + private static readonly short[] _rowScan8X8Neighbors = + [ 0, 0, 0, 0, 1, 1, 0, 0, 8, 8, 2, 2, 8, 8, 9, 9, 3, 3, 16, 16, 10, 10, 16, 16, 4, 4, 17, 17, 24, 24, 11, 11, 18, 18, 25, 25, 24, 24, 5, 5, 12, 12, 19, 19, 32, 32, 26, 26, 6, 6, 33, 33, 32, 32, 20, 20, 27, 27, 40, 40, 13, 13, 34, 34, 40, 40, 41, 41, 28, 28, 35, 35, 48, 48, 21, 21, 42, 42, 14, 14, 48, 48, 36, 36, 49, 49, 43, 43, 29, 29, 56, 56, 22, 22, 50, 50, 57, 57, 44, 44, 37, 37, 51, 51, 30, 30, 58, 58, 52, 52, 45, 45, 59, 59, - 38, 38, 60, 60, 46, 46, 53, 53, 54, 54, 61, 61, 62, 62, 0, 0, - }; + 38, 38, 60, 60, 46, 46, 53, 53, 54, 54, 61, 61, 62, 62, 0, 0 + ]; - private static readonly short[] _defaultScan8X8Neighbors = { + private static readonly short[] _defaultScan8X8Neighbors = + [ 0, 0, 0, 0, 0, 0, 8, 8, 1, 8, 1, 1, 9, 16, 16, 16, 2, 9, 2, 2, 10, 17, 17, 24, 24, 24, 3, 10, 3, 3, 18, 25, 25, 32, 11, 18, 32, 32, 4, 11, 26, 33, 19, 26, 4, 4, 33, 40, 12, 19, 40, 40, 5, 12, 27, 34, 34, 41, 20, 27, 13, 20, 5, 5, 41, 48, 48, 48, 28, 35, 35, 42, 21, 28, 6, 6, 6, 13, 42, 49, 49, 56, 36, 43, 14, 21, 29, 36, 7, 14, 43, 50, 50, 57, 22, 29, 37, 44, 15, 22, 44, 51, 51, 58, 30, 37, 23, 30, 52, 59, 45, 52, 38, 45, - 31, 38, 53, 60, 46, 53, 39, 46, 54, 61, 47, 54, 55, 62, 0, 0, - }; + 31, 38, 53, 60, 46, 53, 39, 46, 54, 61, 47, 54, 55, 62, 0, 0 + ]; - private static readonly short[] _colScan16X16Neighbors = { + private static readonly short[] _colScan16X16Neighbors = + [ 0, 0, 0, 0, 16, 16, 32, 32, 0, 0, 48, 48, 1, 1, 64, 64, 17, 17, 80, 80, 33, 33, 1, 1, 49, 49, 96, 96, 2, 2, 65, 65, 18, 18, 112, 112, 34, 34, 81, 81, 2, 2, 50, 50, 128, @@ -537,10 +569,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 142, 204, 204, 235, 235, 111, 111, 158, 158, 127, 127, 189, 189, 220, 220, 143, 143, 174, 174, 205, 205, 236, 236, 159, 159, 190, 190, 221, 221, 175, 175, 237, 237, 206, 206, 222, 222, 191, 191, 238, 238, 207, 207, 223, 223, - 239, 239, 0, 0, - }; + 239, 239, 0, 0 + ]; - private static readonly short[] _rowScan16X16Neighbors = { + private static readonly short[] _rowScan16X16Neighbors = + [ 0, 0, 0, 0, 1, 1, 0, 0, 2, 2, 16, 16, 3, 3, 17, 17, 16, 16, 4, 4, 32, 32, 18, 18, 5, 5, 33, 33, 32, 32, 19, 19, 48, 48, 6, 6, 34, 34, 20, 20, 49, 49, 48, 48, 7, @@ -575,10 +608,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 142, 173, 173, 158, 158, 249, 249, 234, 234, 204, 204, 219, 219, 174, 174, 189, 189, 250, 250, 220, 220, 190, 190, 205, 205, 235, 235, 206, 206, 236, 236, 251, 251, 221, 221, 252, 252, 222, 222, 237, 237, 238, 238, 253, 253, - 254, 254, 0, 0, - }; + 254, 254, 0, 0 + ]; - private static readonly short[] _defaultScan16X16Neighbors = { + private static readonly short[] _defaultScan16X16Neighbors = + [ 0, 0, 0, 0, 0, 0, 16, 16, 1, 16, 1, 1, 32, 32, 17, 32, 2, 17, 2, 2, 48, 48, 18, 33, 33, 48, 3, 18, 49, 64, 64, 64, 34, 49, 3, 3, 19, 34, 50, 65, 4, 19, 65, 80, 80, @@ -613,10 +647,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 249, 219, 234, 127, 142, 158, 173, 204, 219, 189, 204, 143, 158, 235, 250, 174, 189, 205, 220, 159, 174, 220, 235, 221, 236, 175, 190, 190, 205, 236, 251, 206, 221, 237, 252, 191, 206, 222, 237, 207, 222, 238, 253, 223, 238, - 239, 254, 0, 0, - }; + 239, 254, 0, 0 + ]; - private static readonly short[] _defaultScan32X32Neighbors = { + private static readonly short[] _defaultScan32X32Neighbors = + [ 0, 0, 0, 0, 0, 0, 32, 32, 1, 32, 1, 1, 64, 64, 33, 64, 2, 33, 96, 96, 2, 2, 65, 96, 34, 65, 128, 128, 97, 128, 3, 34, 66, 97, 3, 3, 35, 66, 98, 129, 129, 160, @@ -763,43 +798,50 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 799, 830, 986, 1017, 955, 986, 862, 893, 831, 862, 987, 1018, 863, 894, 892, 923, 924, 955, 893, 924, 956, 987, 925, 956, 894, 925, 988, 1019, 957, 988, 926, 957, 895, 926, 989, 1020, 958, 989, 927, 958, 990, 1021, - 959, 990, 991, 1022, 0, 0, - }; + 959, 990, 991, 1022, 0, 0 + ]; - private static readonly short[] _vp9DefaultIscan4X4 = { - 0, 2, 5, 8, 1, 3, 9, 12, 4, 7, 11, 14, 6, 10, 13, 15, - }; + private static readonly short[] _defaultIscan4X4 = + [ + 0, 2, 5, 8, 1, 3, 9, 12, 4, 7, 11, 14, 6, 10, 13, 15 + ]; - private static readonly short[] _vp9ColIscan4X4 = { - 0, 3, 7, 11, 1, 5, 9, 12, 2, 6, 10, 14, 4, 8, 13, 15, - }; + private static readonly short[] _colIscan4X4 = + [ + 0, 3, 7, 11, 1, 5, 9, 12, 2, 6, 10, 14, 4, 8, 13, 15 + ]; - private static readonly short[] _vp9RowIscan4X4 = { - 0, 1, 3, 5, 2, 4, 6, 9, 7, 8, 11, 13, 10, 12, 14, 15, - }; + private static readonly short[] _rowIscan4X4 = + [ + 0, 1, 3, 5, 2, 4, 6, 9, 7, 8, 11, 13, 10, 12, 14, 15 + ]; - private static readonly short[] _vp9ColIscan8X8 = { + private static readonly short[] _colIscan8X8 = + [ 0, 3, 8, 15, 22, 32, 40, 47, 1, 5, 11, 18, 26, 34, 44, 51, 2, 7, 13, 20, 28, 38, 46, 54, 4, 10, 16, 24, 31, 41, 50, 56, 6, 12, 21, 27, 35, 43, 52, 58, 9, 17, 25, 33, 39, 48, 55, 60, - 14, 23, 30, 37, 45, 53, 59, 62, 19, 29, 36, 42, 49, 57, 61, 63, - }; + 14, 23, 30, 37, 45, 53, 59, 62, 19, 29, 36, 42, 49, 57, 61, 63 + ]; - private static readonly short[] _vp9RowIscan8X8 = { + private static readonly short[] _rowIscan8X8 = + [ 0, 1, 2, 5, 8, 12, 19, 24, 3, 4, 7, 10, 15, 20, 30, 39, 6, 9, 13, 16, 21, 27, 37, 46, 11, 14, 17, 23, 28, 34, 44, 52, 18, 22, 25, 31, 35, 41, 50, 57, 26, 29, 33, 38, 43, 49, 55, 59, - 32, 36, 42, 47, 51, 54, 60, 61, 40, 45, 48, 53, 56, 58, 62, 63, - }; + 32, 36, 42, 47, 51, 54, 60, 61, 40, 45, 48, 53, 56, 58, 62, 63 + ]; - private static readonly short[] _vp9DefaultIscan8X8 = { + private static readonly short[] _defaultIscan8X8 = + [ 0, 2, 5, 9, 14, 22, 31, 37, 1, 4, 8, 13, 19, 26, 38, 44, 3, 6, 10, 17, 24, 30, 42, 49, 7, 11, 15, 21, 29, 36, 47, 53, 12, 16, 20, 27, 34, 43, 52, 57, 18, 23, 28, 35, 41, 48, 56, 60, - 25, 32, 39, 45, 50, 55, 59, 62, 33, 40, 46, 51, 54, 58, 61, 63, - }; + 25, 32, 39, 45, 50, 55, 59, 62, 33, 40, 46, 51, 54, 58, 61, 63 + ]; - private static readonly short[] _vp9ColIscan16X16 = { + private static readonly short[] _colIscan16X16 = + [ 0, 4, 11, 20, 31, 43, 59, 75, 85, 109, 130, 150, 165, 181, 195, 198, 1, 6, 14, 23, 34, 47, 64, 81, 95, 114, 135, 153, 171, 188, 201, 212, 2, 8, 16, 25, 38, 52, 67, 83, 101, 116, 136, 157, 172, 190, 205, 216, @@ -815,10 +857,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 42, 61, 77, 90, 106, 121, 134, 148, 160, 173, 191, 211, 225, 238, 245, 251, 50, 72, 87, 100, 118, 128, 145, 158, 168, 183, 204, 222, 233, 242, 249, 253, 57, 80, 97, 111, 131, 143, 155, 169, 178, 192, 214, 231, 239, 246, 250, 254, - 65, 88, 107, 124, 139, 152, 163, 177, 185, 199, 221, 234, 243, 248, 252, 255, - }; + 65, 88, 107, 124, 139, 152, 163, 177, 185, 199, 221, 234, 243, 248, 252, 255 + ]; - private static readonly short[] _vp9RowIscan16X16 = { + private static readonly short[] _rowIscan16X16 = + [ 0, 1, 2, 4, 6, 9, 12, 17, 22, 29, 36, 43, 54, 64, 76, 86, 3, 5, 7, 11, 15, 19, 25, 32, 38, 48, 59, 68, 84, 99, 115, 130, 8, 10, 13, 18, 23, 27, 33, 42, 51, 60, 72, 88, 103, @@ -836,10 +879,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 156, 166, 172, 180, 189, 199, 200, 210, 220, 228, 238, 242, 249, 251, 152, 163, 171, 183, 186, 193, 201, 211, 214, 218, 227, 236, 245, 247, 252, 253, 158, 173, 187, 194, 198, 209, 213, 217, 225, 229, 235, 241, 248, 250, 254, - 255, - }; + 255 + ]; - private static readonly short[] _vp9DefaultIscan16X16 = { + private static readonly short[] _defaultIscan16X16 = + [ 0, 2, 5, 9, 17, 24, 36, 44, 55, 72, 88, 104, 128, 143, 166, 179, 1, 4, 8, 13, 20, 30, 40, 54, 66, 79, 96, 113, 141, 154, 178, 196, 3, 7, 11, 18, 25, 33, 46, 57, 71, 86, 101, 119, 148, @@ -857,10 +901,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 126, 136, 149, 162, 171, 183, 194, 204, 215, 224, 236, 241, 248, 252, 121, 135, 144, 158, 170, 181, 192, 200, 209, 218, 227, 233, 243, 244, 251, 254, 137, 152, 160, 174, 184, 195, 206, 212, 220, 226, 232, 239, 247, 249, 253, - 255, - }; + 255 + ]; - private static readonly short[] _vp9DefaultIscan32X32 = { + private static readonly short[] _defaultIscan32X32 = + [ 0, 2, 5, 10, 17, 25, 38, 47, 62, 83, 101, 121, 145, 170, 193, 204, 210, 219, 229, 233, 245, 257, 275, 299, 342, 356, 377, 405, 455, 471, 495, 527, 1, 4, 8, 15, 22, 30, 45, @@ -939,8 +984,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 850, 876, 889, 905, 917, 937, 947, 959, 968, 982, 989, 997, 1003, 1011, 1015, 1019, 1022, 496, 528, 552, 568, 618, 646, 667, 681, 724, 748, 766, 778, 814, 834, 849, 859, 888, 904, 916, 924, 946, 958, - 967, 973, 988, 996, 1002, 1006, 1014, 1018, 1021, 1023, - }; + 967, 973, 988, 996, 1002, 1006, 1014, 1018, 1021, 1023 + ]; public class ScanOrder { @@ -956,90 +1001,92 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 } } - public static readonly ScanOrder[] Vp9DefaultScanOrders = { - new(_defaultScan4X4, _vp9DefaultIscan4X4, _defaultScan4X4Neighbors), - new(_defaultScan8X8, _vp9DefaultIscan8X8, _defaultScan8X8Neighbors), - new(_defaultScan16X16, _vp9DefaultIscan16X16, _defaultScan16X16Neighbors), - new(_defaultScan32X32, _vp9DefaultIscan32X32, _defaultScan32X32Neighbors), - }; + public static readonly ScanOrder[] DefaultScanOrders = + [ + new(_defaultScan4X4, _defaultIscan4X4, _defaultScan4X4Neighbors), + new(_defaultScan8X8, _defaultIscan8X8, _defaultScan8X8Neighbors), + new(_defaultScan16X16, _defaultIscan16X16, _defaultScan16X16Neighbors), + new(_defaultScan32X32, _defaultIscan32X32, _defaultScan32X32Neighbors) + ]; - public static readonly ScanOrder[][] Vp9ScanOrders = { - new ScanOrder[] - { // TX_4X4 - new(_defaultScan4X4, _vp9DefaultIscan4X4, _defaultScan4X4Neighbors), - new(_rowScan4X4, _vp9RowIscan4X4, _rowScan4X4Neighbors), - new(_colScan4X4, _vp9ColIscan4X4, _colScan4X4Neighbors), - new(_defaultScan4X4, _vp9DefaultIscan4X4, _defaultScan4X4Neighbors), - }, - new ScanOrder[] - { // TX_8X8 - new(_defaultScan8X8, _vp9DefaultIscan8X8, _defaultScan8X8Neighbors), - new(_rowScan8X8, _vp9RowIscan8X8, _rowScan8X8Neighbors), - new(_colScan8X8, _vp9ColIscan8X8, _colScan8X8Neighbors), - new(_defaultScan8X8, _vp9DefaultIscan8X8, _defaultScan8X8Neighbors), - }, - new ScanOrder[] - { // TX_16X16 - new(_defaultScan16X16, _vp9DefaultIscan16X16, _defaultScan16X16Neighbors), - new(_rowScan16X16, _vp9RowIscan16X16, _rowScan16X16Neighbors), - new(_colScan16X16, _vp9ColIscan16X16, _colScan16X16Neighbors), - new(_defaultScan16X16, _vp9DefaultIscan16X16, _defaultScan16X16Neighbors), - }, - new ScanOrder[] - { // TX_32X32 - new(_defaultScan32X32, _vp9DefaultIscan32X32, _defaultScan32X32Neighbors), - new(_defaultScan32X32, _vp9DefaultIscan32X32, _defaultScan32X32Neighbors), - new(_defaultScan32X32, _vp9DefaultIscan32X32, _defaultScan32X32Neighbors), - new(_defaultScan32X32, _vp9DefaultIscan32X32, _defaultScan32X32Neighbors), - }, + public static readonly ScanOrder[][] ScanOrders = new ScanOrder[][] + { + [ // TX_4X4 + new ScanOrder(_defaultScan4X4, _defaultIscan4X4, _defaultScan4X4Neighbors), + new ScanOrder(_rowScan4X4, _rowIscan4X4, _rowScan4X4Neighbors), + new ScanOrder(_colScan4X4, _colIscan4X4, _colScan4X4Neighbors), + new ScanOrder(_defaultScan4X4, _defaultIscan4X4, _defaultScan4X4Neighbors) + ], + [ // TX_8X8 + new ScanOrder(_defaultScan8X8, _defaultIscan8X8, _defaultScan8X8Neighbors), + new ScanOrder(_rowScan8X8, _rowIscan8X8, _rowScan8X8Neighbors), + new ScanOrder(_colScan8X8, _colIscan8X8, _colScan8X8Neighbors), + new ScanOrder(_defaultScan8X8, _defaultIscan8X8, _defaultScan8X8Neighbors) + ], + [ // TX_16X16 + new ScanOrder(_defaultScan16X16, _defaultIscan16X16, _defaultScan16X16Neighbors), + new ScanOrder(_rowScan16X16, _rowIscan16X16, _rowScan16X16Neighbors), + new ScanOrder(_colScan16X16, _colIscan16X16, _colScan16X16Neighbors), + new ScanOrder(_defaultScan16X16, _defaultIscan16X16, _defaultScan16X16Neighbors) + ], + [ // TX_32X32 + new ScanOrder(_defaultScan32X32, _defaultIscan32X32, _defaultScan32X32Neighbors), + new ScanOrder(_defaultScan32X32, _defaultIscan32X32, _defaultScan32X32Neighbors), + new ScanOrder(_defaultScan32X32, _defaultIscan32X32, _defaultScan32X32Neighbors), + new ScanOrder(_defaultScan32X32, _defaultIscan32X32, _defaultScan32X32Neighbors) + ] }; // Entropy MV - public static readonly sbyte[] Vp9MvJointTree = { - -(sbyte)MvJointType.MvJointZero, 2, -(sbyte)MvJointType.MvJointHnzvz, 4, -(sbyte)MvJointType.MvJointHzvnz, -(sbyte)MvJointType.MvJointHnzvnz, - }; + public static readonly sbyte[] MvJointTree = + [ + -(sbyte)MvJointType.Zero, 2, -(sbyte)MvJointType.Hnzvz, 4, -(sbyte)MvJointType.Hzvnz, -(sbyte)MvJointType.Hnzvnz + ]; - public static readonly sbyte[] Vp9MvClassTree = { - -(sbyte)MvClassType.MvClass0, + public static readonly sbyte[] MvClassTree = + [ + -(sbyte)MvClassType.Class0, 2, - -(sbyte)MvClassType.MvClass1, + -(sbyte)MvClassType.Class1, 4, 6, 8, - -(sbyte)MvClassType.MvClass2, - -(sbyte)MvClassType.MvClass3, + -(sbyte)MvClassType.Class2, + -(sbyte)MvClassType.Class3, 10, 12, - -(sbyte)MvClassType.MvClass4, - -(sbyte)MvClassType.MvClass5, - -(sbyte)MvClassType.MvClass6, + -(sbyte)MvClassType.Class4, + -(sbyte)MvClassType.Class5, + -(sbyte)MvClassType.Class6, 14, 16, 18, - -(sbyte)MvClassType.MvClass7, - -(sbyte)MvClassType.MvClass8, - -(sbyte)MvClassType.MvClass9, - -(sbyte)MvClassType.MvClass10, - }; + -(sbyte)MvClassType.Class7, + -(sbyte)MvClassType.Class8, + -(sbyte)MvClassType.Class9, + -(sbyte)MvClassType.Class10 + ]; - public static ReadOnlySpan Vp9MvFPTree => new sbyte[] { -0, 2, -1, 4, -2, -3 }; + public static ReadOnlySpan MvFpTree => [-0, 2, -1, 4, -2, -3]; // Entropy - public static ReadOnlySpan Vp9Cat1Prob => new byte[] { 159 }; - public static ReadOnlySpan Vp9Cat2Prob => new byte[] { 165, 145 }; - public static ReadOnlySpan Vp9Cat3Prob => new byte[] { 173, 148, 140 }; - public static ReadOnlySpan Vp9Cat4Prob => new byte[] { 176, 155, 140, 135 }; - public static ReadOnlySpan Vp9Cat5Prob => new byte[] { 180, 157, 141, 134, 130 }; - public static ReadOnlySpan Vp9Cat6Prob => new byte[] { 254, 254, 254, 252, 249, 243, 230, 196, 177, 153, 140, 133, 130, 129 }; + public static ReadOnlySpan Cat1Prob => [159]; + public static ReadOnlySpan Cat2Prob => [165, 145]; + public static ReadOnlySpan Cat3Prob => [173, 148, 140]; + public static ReadOnlySpan Cat4Prob => [176, 155, 140, 135]; + public static ReadOnlySpan Cat5Prob => [180, 157, 141, 134, 130]; + public static ReadOnlySpan Cat6Prob => [254, 254, 254, 252, 249, 243, 230, 196, 177, 153, 140, 133, 130, 129 + ]; - public static ReadOnlySpan Vp9Cat6ProbHigh12 => new byte[] - { - 255, 255, 255, 255, 254, 254, 54, 252, 249, 243, 230, 196, 177, 153, 140, 133, 130, 129, - }; + public static ReadOnlySpan Cat6ProbHigh12 => + [ + 255, 255, 255, 255, 254, 254, 54, 252, 249, 243, 230, 196, 177, 153, 140, 133, 130, 129 + ]; - private static readonly byte[] _vp9CoefbandTrans8X8Plus = { + private static readonly byte[] _coefbandTrans8X8Plus = + [ 0, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, // Beyond MAXBAND_INDEX+1 all values are filled as 5 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, @@ -1080,308 +1127,228 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - }; + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 + ]; - private static ReadOnlySpan Vp9CoefbandTrans4X4 => new byte[] - { - 0, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 5, 5, 5, - }; + private static ReadOnlySpan CoefbandTrans4X4 => + [ + 0, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 5, 5, 5 + ]; public static ReadOnlySpan GetBandTranslate(TxSize txSize) { - return txSize == TxSize.Tx4x4 ? Vp9CoefbandTrans4X4 : _vp9CoefbandTrans8X8Plus; + return txSize == TxSize.Tx4X4 ? CoefbandTrans4X4 : _coefbandTrans8X8Plus; } - public static readonly byte[][] Vp9Pareto8Full = { - new byte[] { 3, 86, 128, 6, 86, 23, 88, 29 }, - new byte[] { 6, 86, 128, 11, 87, 42, 91, 52 }, - new byte[] { 9, 86, 129, 17, 88, 61, 94, 76 }, - new byte[] { 12, 86, 129, 22, 88, 77, 97, 93 }, - new byte[] { 15, 87, 129, 28, 89, 93, 100, 110 }, - new byte[] { 17, 87, 129, 33, 90, 105, 103, 123 }, - new byte[] { 20, 88, 130, 38, 91, 118, 106, 136 }, - new byte[] { 23, 88, 130, 43, 91, 128, 108, 146 }, - new byte[] { 26, 89, 131, 48, 92, 139, 111, 156 }, - new byte[] { 28, 89, 131, 53, 93, 147, 114, 163 }, - new byte[] { 31, 90, 131, 58, 94, 156, 117, 171 }, - new byte[] { 34, 90, 131, 62, 94, 163, 119, 177 }, - new byte[] { 37, 90, 132, 66, 95, 171, 122, 184 }, - new byte[] { 39, 90, 132, 70, 96, 177, 124, 189 }, - new byte[] { 42, 91, 132, 75, 97, 183, 127, 194 }, - new byte[] { 44, 91, 132, 79, 97, 188, 129, 198 }, - new byte[] { 47, 92, 133, 83, 98, 193, 132, 202 }, - new byte[] { 49, 92, 133, 86, 99, 197, 134, 205 }, - new byte[] { 52, 93, 133, 90, 100, 201, 137, 208 }, - new byte[] { 54, 93, 133, 94, 100, 204, 139, 211 }, - new byte[] { 57, 94, 134, 98, 101, 208, 142, 214 }, - new byte[] { 59, 94, 134, 101, 102, 211, 144, 216 }, - new byte[] { 62, 94, 135, 105, 103, 214, 146, 218 }, - new byte[] { 64, 94, 135, 108, 103, 216, 148, 220 }, - new byte[] { 66, 95, 135, 111, 104, 219, 151, 222 }, - new byte[] { 68, 95, 135, 114, 105, 221, 153, 223 }, - new byte[] { 71, 96, 136, 117, 106, 224, 155, 225 }, - new byte[] { 73, 96, 136, 120, 106, 225, 157, 226 }, - new byte[] { 76, 97, 136, 123, 107, 227, 159, 228 }, - new byte[] { 78, 97, 136, 126, 108, 229, 160, 229 }, - new byte[] { 80, 98, 137, 129, 109, 231, 162, 231 }, - new byte[] { 82, 98, 137, 131, 109, 232, 164, 232 }, - new byte[] { 84, 98, 138, 134, 110, 234, 166, 233 }, - new byte[] { 86, 98, 138, 137, 111, 235, 168, 234 }, - new byte[] { 89, 99, 138, 140, 112, 236, 170, 235 }, - new byte[] { 91, 99, 138, 142, 112, 237, 171, 235 }, - new byte[] { 93, 100, 139, 145, 113, 238, 173, 236 }, - new byte[] { 95, 100, 139, 147, 114, 239, 174, 237 }, - new byte[] { 97, 101, 140, 149, 115, 240, 176, 238 }, - new byte[] { 99, 101, 140, 151, 115, 241, 177, 238 }, - new byte[] { 101, 102, 140, 154, 116, 242, 179, 239 }, - new byte[] { 103, 102, 140, 156, 117, 242, 180, 239 }, - new byte[] { 105, 103, 141, 158, 118, 243, 182, 240 }, - new byte[] { 107, 103, 141, 160, 118, 243, 183, 240 }, - new byte[] { 109, 104, 141, 162, 119, 244, 185, 241 }, - new byte[] { 111, 104, 141, 164, 119, 244, 186, 241 }, - new byte[] { 113, 104, 142, 166, 120, 245, 187, 242 }, - new byte[] { 114, 104, 142, 168, 121, 245, 188, 242 }, - new byte[] { 116, 105, 143, 170, 122, 246, 190, 243 }, - new byte[] { 118, 105, 143, 171, 122, 246, 191, 243 }, - new byte[] { 120, 106, 143, 173, 123, 247, 192, 244 }, - new byte[] { 121, 106, 143, 175, 124, 247, 193, 244 }, - new byte[] { 123, 107, 144, 177, 125, 248, 195, 244 }, - new byte[] { 125, 107, 144, 178, 125, 248, 196, 244 }, - new byte[] { 127, 108, 145, 180, 126, 249, 197, 245 }, - new byte[] { 128, 108, 145, 181, 127, 249, 198, 245 }, - new byte[] { 130, 109, 145, 183, 128, 249, 199, 245 }, - new byte[] { 132, 109, 145, 184, 128, 249, 200, 245 }, - new byte[] { 134, 110, 146, 186, 129, 250, 201, 246 }, - new byte[] { 135, 110, 146, 187, 130, 250, 202, 246 }, - new byte[] { 137, 111, 147, 189, 131, 251, 203, 246 }, - new byte[] { 138, 111, 147, 190, 131, 251, 204, 246 }, - new byte[] { 140, 112, 147, 192, 132, 251, 205, 247 }, - new byte[] { 141, 112, 147, 193, 132, 251, 206, 247 }, - new byte[] { 143, 113, 148, 194, 133, 251, 207, 247 }, - new byte[] { 144, 113, 148, 195, 134, 251, 207, 247 }, - new byte[] { 146, 114, 149, 197, 135, 252, 208, 248 }, - new byte[] { 147, 114, 149, 198, 135, 252, 209, 248 }, - new byte[] { 149, 115, 149, 199, 136, 252, 210, 248 }, - new byte[] { 150, 115, 149, 200, 137, 252, 210, 248 }, - new byte[] { 152, 115, 150, 201, 138, 252, 211, 248 }, - new byte[] { 153, 115, 150, 202, 138, 252, 212, 248 }, - new byte[] { 155, 116, 151, 204, 139, 253, 213, 249 }, - new byte[] { 156, 116, 151, 205, 139, 253, 213, 249 }, - new byte[] { 158, 117, 151, 206, 140, 253, 214, 249 }, - new byte[] { 159, 117, 151, 207, 141, 253, 215, 249 }, - new byte[] { 161, 118, 152, 208, 142, 253, 216, 249 }, - new byte[] { 162, 118, 152, 209, 142, 253, 216, 249 }, - new byte[] { 163, 119, 153, 210, 143, 253, 217, 249 }, - new byte[] { 164, 119, 153, 211, 143, 253, 217, 249 }, - new byte[] { 166, 120, 153, 212, 144, 254, 218, 250 }, - new byte[] { 167, 120, 153, 212, 145, 254, 219, 250 }, - new byte[] { 168, 121, 154, 213, 146, 254, 220, 250 }, - new byte[] { 169, 121, 154, 214, 146, 254, 220, 250 }, - new byte[] { 171, 122, 155, 215, 147, 254, 221, 250 }, - new byte[] { 172, 122, 155, 216, 147, 254, 221, 250 }, - new byte[] { 173, 123, 155, 217, 148, 254, 222, 250 }, - new byte[] { 174, 123, 155, 217, 149, 254, 222, 250 }, - new byte[] { 176, 124, 156, 218, 150, 254, 223, 250 }, - new byte[] { 177, 124, 156, 219, 150, 254, 223, 250 }, - new byte[] { 178, 125, 157, 220, 151, 254, 224, 251 }, - new byte[] { 179, 125, 157, 220, 151, 254, 224, 251 }, - new byte[] { 180, 126, 157, 221, 152, 254, 225, 251 }, - new byte[] { 181, 126, 157, 221, 152, 254, 225, 251 }, - new byte[] { 183, 127, 158, 222, 153, 254, 226, 251 }, - new byte[] { 184, 127, 158, 223, 154, 254, 226, 251 }, - new byte[] { 185, 128, 159, 224, 155, 255, 227, 251 }, - new byte[] { 186, 128, 159, 224, 155, 255, 227, 251 }, - new byte[] { 187, 129, 160, 225, 156, 255, 228, 251 }, - new byte[] { 188, 130, 160, 225, 156, 255, 228, 251 }, - new byte[] { 189, 131, 160, 226, 157, 255, 228, 251 }, - new byte[] { 190, 131, 160, 226, 158, 255, 228, 251 }, - new byte[] { 191, 132, 161, 227, 159, 255, 229, 251 }, - new byte[] { 192, 132, 161, 227, 159, 255, 229, 251 }, - new byte[] { 193, 133, 162, 228, 160, 255, 230, 252 }, - new byte[] { 194, 133, 162, 229, 160, 255, 230, 252 }, - new byte[] { 195, 134, 163, 230, 161, 255, 231, 252 }, - new byte[] { 196, 134, 163, 230, 161, 255, 231, 252 }, - new byte[] { 197, 135, 163, 231, 162, 255, 231, 252 }, - new byte[] { 198, 135, 163, 231, 162, 255, 231, 252 }, - new byte[] { 199, 136, 164, 232, 163, 255, 232, 252 }, - new byte[] { 200, 136, 164, 232, 164, 255, 232, 252 }, - new byte[] { 201, 137, 165, 233, 165, 255, 233, 252 }, - new byte[] { 201, 137, 165, 233, 165, 255, 233, 252 }, - new byte[] { 202, 138, 166, 233, 166, 255, 233, 252 }, - new byte[] { 203, 138, 166, 233, 166, 255, 233, 252 }, - new byte[] { 204, 139, 166, 234, 167, 255, 234, 252 }, - new byte[] { 205, 139, 166, 234, 167, 255, 234, 252 }, - new byte[] { 206, 140, 167, 235, 168, 255, 235, 252 }, - new byte[] { 206, 140, 167, 235, 168, 255, 235, 252 }, - new byte[] { 207, 141, 168, 236, 169, 255, 235, 252 }, - new byte[] { 208, 141, 168, 236, 170, 255, 235, 252 }, - new byte[] { 209, 142, 169, 237, 171, 255, 236, 252 }, - new byte[] { 209, 143, 169, 237, 171, 255, 236, 252 }, - new byte[] { 210, 144, 169, 237, 172, 255, 236, 252 }, - new byte[] { 211, 144, 169, 237, 172, 255, 236, 252 }, - new byte[] { 212, 145, 170, 238, 173, 255, 237, 252 }, - new byte[] { 213, 145, 170, 238, 173, 255, 237, 252 }, - new byte[] { 214, 146, 171, 239, 174, 255, 237, 253 }, - new byte[] { 214, 146, 171, 239, 174, 255, 237, 253 }, - new byte[] { 215, 147, 172, 240, 175, 255, 238, 253 }, - new byte[] { 215, 147, 172, 240, 175, 255, 238, 253 }, - new byte[] { 216, 148, 173, 240, 176, 255, 238, 253 }, - new byte[] { 217, 148, 173, 240, 176, 255, 238, 253 }, - new byte[] { 218, 149, 173, 241, 177, 255, 239, 253 }, - new byte[] { 218, 149, 173, 241, 178, 255, 239, 253 }, - new byte[] { 219, 150, 174, 241, 179, 255, 239, 253 }, - new byte[] { 219, 151, 174, 241, 179, 255, 239, 253 }, - new byte[] { 220, 152, 175, 242, 180, 255, 240, 253 }, - new byte[] { 221, 152, 175, 242, 180, 255, 240, 253 }, - new byte[] { 222, 153, 176, 242, 181, 255, 240, 253 }, - new byte[] { 222, 153, 176, 242, 181, 255, 240, 253 }, - new byte[] { 223, 154, 177, 243, 182, 255, 240, 253 }, - new byte[] { 223, 154, 177, 243, 182, 255, 240, 253 }, - new byte[] { 224, 155, 178, 244, 183, 255, 241, 253 }, - new byte[] { 224, 155, 178, 244, 183, 255, 241, 253 }, - new byte[] { 225, 156, 178, 244, 184, 255, 241, 253 }, - new byte[] { 225, 157, 178, 244, 184, 255, 241, 253 }, - new byte[] { 226, 158, 179, 244, 185, 255, 242, 253 }, - new byte[] { 227, 158, 179, 244, 185, 255, 242, 253 }, - new byte[] { 228, 159, 180, 245, 186, 255, 242, 253 }, - new byte[] { 228, 159, 180, 245, 186, 255, 242, 253 }, - new byte[] { 229, 160, 181, 245, 187, 255, 242, 253 }, - new byte[] { 229, 160, 181, 245, 187, 255, 242, 253 }, - new byte[] { 230, 161, 182, 246, 188, 255, 243, 253 }, - new byte[] { 230, 162, 182, 246, 188, 255, 243, 253 }, - new byte[] { 231, 163, 183, 246, 189, 255, 243, 253 }, - new byte[] { 231, 163, 183, 246, 189, 255, 243, 253 }, - new byte[] { 232, 164, 184, 247, 190, 255, 243, 253 }, - new byte[] { 232, 164, 184, 247, 190, 255, 243, 253 }, - new byte[] { 233, 165, 185, 247, 191, 255, 244, 253 }, - new byte[] { 233, 165, 185, 247, 191, 255, 244, 253 }, - new byte[] { 234, 166, 185, 247, 192, 255, 244, 253 }, - new byte[] { 234, 167, 185, 247, 192, 255, 244, 253 }, - new byte[] { 235, 168, 186, 248, 193, 255, 244, 253 }, - new byte[] { 235, 168, 186, 248, 193, 255, 244, 253 }, - new byte[] { 236, 169, 187, 248, 194, 255, 244, 253 }, - new byte[] { 236, 169, 187, 248, 194, 255, 244, 253 }, - new byte[] { 236, 170, 188, 248, 195, 255, 245, 253 }, - new byte[] { 236, 170, 188, 248, 195, 255, 245, 253 }, - new byte[] { 237, 171, 189, 249, 196, 255, 245, 254 }, - new byte[] { 237, 172, 189, 249, 196, 255, 245, 254 }, - new byte[] { 238, 173, 190, 249, 197, 255, 245, 254 }, - new byte[] { 238, 173, 190, 249, 197, 255, 245, 254 }, - new byte[] { 239, 174, 191, 249, 198, 255, 245, 254 }, - new byte[] { 239, 174, 191, 249, 198, 255, 245, 254 }, - new byte[] { 240, 175, 192, 249, 199, 255, 246, 254 }, - new byte[] { 240, 176, 192, 249, 199, 255, 246, 254 }, - new byte[] { 240, 177, 193, 250, 200, 255, 246, 254 }, - new byte[] { 240, 177, 193, 250, 200, 255, 246, 254 }, - new byte[] { 241, 178, 194, 250, 201, 255, 246, 254 }, - new byte[] { 241, 178, 194, 250, 201, 255, 246, 254 }, - new byte[] { 242, 179, 195, 250, 202, 255, 246, 254 }, - new byte[] { 242, 180, 195, 250, 202, 255, 246, 254 }, - new byte[] { 242, 181, 196, 250, 203, 255, 247, 254 }, - new byte[] { 242, 181, 196, 250, 203, 255, 247, 254 }, - new byte[] { 243, 182, 197, 251, 204, 255, 247, 254 }, - new byte[] { 243, 183, 197, 251, 204, 255, 247, 254 }, - new byte[] { 244, 184, 198, 251, 205, 255, 247, 254 }, - new byte[] { 244, 184, 198, 251, 205, 255, 247, 254 }, - new byte[] { 244, 185, 199, 251, 206, 255, 247, 254 }, - new byte[] { 244, 185, 199, 251, 206, 255, 247, 254 }, - new byte[] { 245, 186, 200, 251, 207, 255, 247, 254 }, - new byte[] { 245, 187, 200, 251, 207, 255, 247, 254 }, - new byte[] { 246, 188, 201, 252, 207, 255, 248, 254 }, - new byte[] { 246, 188, 201, 252, 207, 255, 248, 254 }, - new byte[] { 246, 189, 202, 252, 208, 255, 248, 254 }, - new byte[] { 246, 190, 202, 252, 208, 255, 248, 254 }, - new byte[] { 247, 191, 203, 252, 209, 255, 248, 254 }, - new byte[] { 247, 191, 203, 252, 209, 255, 248, 254 }, - new byte[] { 247, 192, 204, 252, 210, 255, 248, 254 }, - new byte[] { 247, 193, 204, 252, 210, 255, 248, 254 }, - new byte[] { 248, 194, 205, 252, 211, 255, 248, 254 }, - new byte[] { 248, 194, 205, 252, 211, 255, 248, 254 }, - new byte[] { 248, 195, 206, 252, 212, 255, 249, 254 }, - new byte[] { 248, 196, 206, 252, 212, 255, 249, 254 }, - new byte[] { 249, 197, 207, 253, 213, 255, 249, 254 }, - new byte[] { 249, 197, 207, 253, 213, 255, 249, 254 }, - new byte[] { 249, 198, 208, 253, 214, 255, 249, 254 }, - new byte[] { 249, 199, 209, 253, 214, 255, 249, 254 }, - new byte[] { 250, 200, 210, 253, 215, 255, 249, 254 }, - new byte[] { 250, 200, 210, 253, 215, 255, 249, 254 }, - new byte[] { 250, 201, 211, 253, 215, 255, 249, 254 }, - new byte[] { 250, 202, 211, 253, 215, 255, 249, 254 }, - new byte[] { 250, 203, 212, 253, 216, 255, 249, 254 }, - new byte[] { 250, 203, 212, 253, 216, 255, 249, 254 }, - new byte[] { 251, 204, 213, 253, 217, 255, 250, 254 }, - new byte[] { 251, 205, 213, 253, 217, 255, 250, 254 }, - new byte[] { 251, 206, 214, 254, 218, 255, 250, 254 }, - new byte[] { 251, 206, 215, 254, 218, 255, 250, 254 }, - new byte[] { 252, 207, 216, 254, 219, 255, 250, 254 }, - new byte[] { 252, 208, 216, 254, 219, 255, 250, 254 }, - new byte[] { 252, 209, 217, 254, 220, 255, 250, 254 }, - new byte[] { 252, 210, 217, 254, 220, 255, 250, 254 }, - new byte[] { 252, 211, 218, 254, 221, 255, 250, 254 }, - new byte[] { 252, 212, 218, 254, 221, 255, 250, 254 }, - new byte[] { 253, 213, 219, 254, 222, 255, 250, 254 }, - new byte[] { 253, 213, 220, 254, 222, 255, 250, 254 }, - new byte[] { 253, 214, 221, 254, 223, 255, 250, 254 }, - new byte[] { 253, 215, 221, 254, 223, 255, 250, 254 }, - new byte[] { 253, 216, 222, 254, 224, 255, 251, 254 }, - new byte[] { 253, 217, 223, 254, 224, 255, 251, 254 }, - new byte[] { 253, 218, 224, 254, 225, 255, 251, 254 }, - new byte[] { 253, 219, 224, 254, 225, 255, 251, 254 }, - new byte[] { 254, 220, 225, 254, 225, 255, 251, 254 }, - new byte[] { 254, 221, 226, 254, 225, 255, 251, 254 }, - new byte[] { 254, 222, 227, 255, 226, 255, 251, 254 }, - new byte[] { 254, 223, 227, 255, 226, 255, 251, 254 }, - new byte[] { 254, 224, 228, 255, 227, 255, 251, 254 }, - new byte[] { 254, 225, 229, 255, 227, 255, 251, 254 }, - new byte[] { 254, 226, 230, 255, 228, 255, 251, 254 }, - new byte[] { 254, 227, 230, 255, 229, 255, 251, 254 }, - new byte[] { 255, 228, 231, 255, 230, 255, 251, 254 }, - new byte[] { 255, 229, 232, 255, 230, 255, 251, 254 }, - new byte[] { 255, 230, 233, 255, 231, 255, 252, 254 }, - new byte[] { 255, 231, 234, 255, 231, 255, 252, 254 }, - new byte[] { 255, 232, 235, 255, 232, 255, 252, 254 }, - new byte[] { 255, 233, 236, 255, 232, 255, 252, 254 }, - new byte[] { 255, 235, 237, 255, 233, 255, 252, 254 }, - new byte[] { 255, 236, 238, 255, 234, 255, 252, 254 }, - new byte[] { 255, 238, 240, 255, 235, 255, 252, 255 }, - new byte[] { 255, 239, 241, 255, 235, 255, 252, 254 }, - new byte[] { 255, 241, 243, 255, 236, 255, 252, 254 }, - new byte[] { 255, 243, 245, 255, 237, 255, 252, 254 }, - new byte[] { 255, 246, 247, 255, 239, 255, 253, 255 }, + public static readonly byte[][] Pareto8Full = new byte[][] + { + [3, 86, 128, 6, 86, 23, 88, 29], [6, 86, 128, 11, 87, 42, 91, 52], [9, 86, 129, 17, 88, 61, 94, 76], [12, 86, 129, 22, 88, 77, 97, 93 + ], + [15, 87, 129, 28, 89, 93, 100, 110], [17, 87, 129, 33, 90, 105, 103, 123], [20, 88, 130, 38, 91, 118, 106, 136 + ], + [23, 88, 130, 43, 91, 128, 108, 146], [26, 89, 131, 48, 92, 139, 111, 156], [28, 89, 131, 53, 93, 147, 114, 163 + ], + [31, 90, 131, 58, 94, 156, 117, 171], [34, 90, 131, 62, 94, 163, 119, 177], [37, 90, 132, 66, 95, 171, 122, 184 + ], + [39, 90, 132, 70, 96, 177, 124, 189], [42, 91, 132, 75, 97, 183, 127, 194], [44, 91, 132, 79, 97, 188, 129, 198 + ], + [47, 92, 133, 83, 98, 193, 132, 202], [49, 92, 133, 86, 99, 197, 134, 205], [52, 93, 133, 90, 100, 201, 137, 208 + ], + [54, 93, 133, 94, 100, 204, 139, 211], [57, 94, 134, 98, 101, 208, 142, 214], [59, 94, 134, 101, 102, 211, 144, 216 + ], + [62, 94, 135, 105, 103, 214, 146, 218], [64, 94, 135, 108, 103, 216, 148, 220], [66, 95, 135, 111, 104, 219, 151, 222 + ], + [68, 95, 135, 114, 105, 221, 153, 223], [71, 96, 136, 117, 106, 224, 155, 225], [73, 96, 136, 120, 106, 225, 157, 226 + ], + [76, 97, 136, 123, 107, 227, 159, 228], [78, 97, 136, 126, 108, 229, 160, 229], [80, 98, 137, 129, 109, 231, 162, 231 + ], + [82, 98, 137, 131, 109, 232, 164, 232], [84, 98, 138, 134, 110, 234, 166, 233], [86, 98, 138, 137, 111, 235, 168, 234 + ], + [89, 99, 138, 140, 112, 236, 170, 235], [91, 99, 138, 142, 112, 237, 171, 235], [93, 100, 139, 145, 113, 238, 173, 236 + ], + [95, 100, 139, 147, 114, 239, 174, 237], [97, 101, 140, 149, 115, 240, 176, 238], [99, 101, 140, 151, 115, 241, 177, 238 + ], + [101, 102, 140, 154, 116, 242, 179, 239], [103, 102, 140, 156, 117, 242, 180, 239], [105, 103, 141, 158, 118, 243, 182, 240 + ], + [107, 103, 141, 160, 118, 243, 183, 240], [109, 104, 141, 162, 119, 244, 185, 241], [111, 104, 141, 164, 119, 244, 186, 241 + ], + [113, 104, 142, 166, 120, 245, 187, 242], [114, 104, 142, 168, 121, 245, 188, 242], [116, 105, 143, 170, 122, 246, 190, 243 + ], + [118, 105, 143, 171, 122, 246, 191, 243], [120, 106, 143, 173, 123, 247, 192, 244], [121, 106, 143, 175, 124, 247, 193, 244 + ], + [123, 107, 144, 177, 125, 248, 195, 244], [125, 107, 144, 178, 125, 248, 196, 244], [127, 108, 145, 180, 126, 249, 197, 245 + ], + [128, 108, 145, 181, 127, 249, 198, 245], [130, 109, 145, 183, 128, 249, 199, 245], [132, 109, 145, 184, 128, 249, 200, 245 + ], + [134, 110, 146, 186, 129, 250, 201, 246], [135, 110, 146, 187, 130, 250, 202, 246], [137, 111, 147, 189, 131, 251, 203, 246 + ], + [138, 111, 147, 190, 131, 251, 204, 246], [140, 112, 147, 192, 132, 251, 205, 247], [141, 112, 147, 193, 132, 251, 206, 247 + ], + [143, 113, 148, 194, 133, 251, 207, 247], [144, 113, 148, 195, 134, 251, 207, 247], [146, 114, 149, 197, 135, 252, 208, 248 + ], + [147, 114, 149, 198, 135, 252, 209, 248], [149, 115, 149, 199, 136, 252, 210, 248], [150, 115, 149, 200, 137, 252, 210, 248 + ], + [152, 115, 150, 201, 138, 252, 211, 248], [153, 115, 150, 202, 138, 252, 212, 248], [155, 116, 151, 204, 139, 253, 213, 249 + ], + [156, 116, 151, 205, 139, 253, 213, 249], [158, 117, 151, 206, 140, 253, 214, 249], [159, 117, 151, 207, 141, 253, 215, 249 + ], + [161, 118, 152, 208, 142, 253, 216, 249], [162, 118, 152, 209, 142, 253, 216, 249], [163, 119, 153, 210, 143, 253, 217, 249 + ], + [164, 119, 153, 211, 143, 253, 217, 249], [166, 120, 153, 212, 144, 254, 218, 250], [167, 120, 153, 212, 145, 254, 219, 250 + ], + [168, 121, 154, 213, 146, 254, 220, 250], [169, 121, 154, 214, 146, 254, 220, 250], [171, 122, 155, 215, 147, 254, 221, 250 + ], + [172, 122, 155, 216, 147, 254, 221, 250], [173, 123, 155, 217, 148, 254, 222, 250], [174, 123, 155, 217, 149, 254, 222, 250 + ], + [176, 124, 156, 218, 150, 254, 223, 250], [177, 124, 156, 219, 150, 254, 223, 250], [178, 125, 157, 220, 151, 254, 224, 251 + ], + [179, 125, 157, 220, 151, 254, 224, 251], [180, 126, 157, 221, 152, 254, 225, 251], [181, 126, 157, 221, 152, 254, 225, 251 + ], + [183, 127, 158, 222, 153, 254, 226, 251], [184, 127, 158, 223, 154, 254, 226, 251], [185, 128, 159, 224, 155, 255, 227, 251 + ], + [186, 128, 159, 224, 155, 255, 227, 251], [187, 129, 160, 225, 156, 255, 228, 251], [188, 130, 160, 225, 156, 255, 228, 251 + ], + [189, 131, 160, 226, 157, 255, 228, 251], [190, 131, 160, 226, 158, 255, 228, 251], [191, 132, 161, 227, 159, 255, 229, 251 + ], + [192, 132, 161, 227, 159, 255, 229, 251], [193, 133, 162, 228, 160, 255, 230, 252], [194, 133, 162, 229, 160, 255, 230, 252 + ], + [195, 134, 163, 230, 161, 255, 231, 252], [196, 134, 163, 230, 161, 255, 231, 252], [197, 135, 163, 231, 162, 255, 231, 252 + ], + [198, 135, 163, 231, 162, 255, 231, 252], [199, 136, 164, 232, 163, 255, 232, 252], [200, 136, 164, 232, 164, 255, 232, 252 + ], + [201, 137, 165, 233, 165, 255, 233, 252], [201, 137, 165, 233, 165, 255, 233, 252], [202, 138, 166, 233, 166, 255, 233, 252 + ], + [203, 138, 166, 233, 166, 255, 233, 252], [204, 139, 166, 234, 167, 255, 234, 252], [205, 139, 166, 234, 167, 255, 234, 252 + ], + [206, 140, 167, 235, 168, 255, 235, 252], [206, 140, 167, 235, 168, 255, 235, 252], [207, 141, 168, 236, 169, 255, 235, 252 + ], + [208, 141, 168, 236, 170, 255, 235, 252], [209, 142, 169, 237, 171, 255, 236, 252], [209, 143, 169, 237, 171, 255, 236, 252 + ], + [210, 144, 169, 237, 172, 255, 236, 252], [211, 144, 169, 237, 172, 255, 236, 252], [212, 145, 170, 238, 173, 255, 237, 252 + ], + [213, 145, 170, 238, 173, 255, 237, 252], [214, 146, 171, 239, 174, 255, 237, 253], [214, 146, 171, 239, 174, 255, 237, 253 + ], + [215, 147, 172, 240, 175, 255, 238, 253], [215, 147, 172, 240, 175, 255, 238, 253], [216, 148, 173, 240, 176, 255, 238, 253 + ], + [217, 148, 173, 240, 176, 255, 238, 253], [218, 149, 173, 241, 177, 255, 239, 253], [218, 149, 173, 241, 178, 255, 239, 253 + ], + [219, 150, 174, 241, 179, 255, 239, 253], [219, 151, 174, 241, 179, 255, 239, 253], [220, 152, 175, 242, 180, 255, 240, 253 + ], + [221, 152, 175, 242, 180, 255, 240, 253], [222, 153, 176, 242, 181, 255, 240, 253], [222, 153, 176, 242, 181, 255, 240, 253 + ], + [223, 154, 177, 243, 182, 255, 240, 253], [223, 154, 177, 243, 182, 255, 240, 253], [224, 155, 178, 244, 183, 255, 241, 253 + ], + [224, 155, 178, 244, 183, 255, 241, 253], [225, 156, 178, 244, 184, 255, 241, 253], [225, 157, 178, 244, 184, 255, 241, 253 + ], + [226, 158, 179, 244, 185, 255, 242, 253], [227, 158, 179, 244, 185, 255, 242, 253], [228, 159, 180, 245, 186, 255, 242, 253 + ], + [228, 159, 180, 245, 186, 255, 242, 253], [229, 160, 181, 245, 187, 255, 242, 253], [229, 160, 181, 245, 187, 255, 242, 253 + ], + [230, 161, 182, 246, 188, 255, 243, 253], [230, 162, 182, 246, 188, 255, 243, 253], [231, 163, 183, 246, 189, 255, 243, 253 + ], + [231, 163, 183, 246, 189, 255, 243, 253], [232, 164, 184, 247, 190, 255, 243, 253], [232, 164, 184, 247, 190, 255, 243, 253 + ], + [233, 165, 185, 247, 191, 255, 244, 253], [233, 165, 185, 247, 191, 255, 244, 253], [234, 166, 185, 247, 192, 255, 244, 253 + ], + [234, 167, 185, 247, 192, 255, 244, 253], [235, 168, 186, 248, 193, 255, 244, 253], [235, 168, 186, 248, 193, 255, 244, 253 + ], + [236, 169, 187, 248, 194, 255, 244, 253], [236, 169, 187, 248, 194, 255, 244, 253], [236, 170, 188, 248, 195, 255, 245, 253 + ], + [236, 170, 188, 248, 195, 255, 245, 253], [237, 171, 189, 249, 196, 255, 245, 254], [237, 172, 189, 249, 196, 255, 245, 254 + ], + [238, 173, 190, 249, 197, 255, 245, 254], [238, 173, 190, 249, 197, 255, 245, 254], [239, 174, 191, 249, 198, 255, 245, 254 + ], + [239, 174, 191, 249, 198, 255, 245, 254], [240, 175, 192, 249, 199, 255, 246, 254], [240, 176, 192, 249, 199, 255, 246, 254 + ], + [240, 177, 193, 250, 200, 255, 246, 254], [240, 177, 193, 250, 200, 255, 246, 254], [241, 178, 194, 250, 201, 255, 246, 254 + ], + [241, 178, 194, 250, 201, 255, 246, 254], [242, 179, 195, 250, 202, 255, 246, 254], [242, 180, 195, 250, 202, 255, 246, 254 + ], + [242, 181, 196, 250, 203, 255, 247, 254], [242, 181, 196, 250, 203, 255, 247, 254], [243, 182, 197, 251, 204, 255, 247, 254 + ], + [243, 183, 197, 251, 204, 255, 247, 254], [244, 184, 198, 251, 205, 255, 247, 254], [244, 184, 198, 251, 205, 255, 247, 254 + ], + [244, 185, 199, 251, 206, 255, 247, 254], [244, 185, 199, 251, 206, 255, 247, 254], [245, 186, 200, 251, 207, 255, 247, 254 + ], + [245, 187, 200, 251, 207, 255, 247, 254], [246, 188, 201, 252, 207, 255, 248, 254], [246, 188, 201, 252, 207, 255, 248, 254 + ], + [246, 189, 202, 252, 208, 255, 248, 254], [246, 190, 202, 252, 208, 255, 248, 254], [247, 191, 203, 252, 209, 255, 248, 254 + ], + [247, 191, 203, 252, 209, 255, 248, 254], [247, 192, 204, 252, 210, 255, 248, 254], [247, 193, 204, 252, 210, 255, 248, 254 + ], + [248, 194, 205, 252, 211, 255, 248, 254], [248, 194, 205, 252, 211, 255, 248, 254], [248, 195, 206, 252, 212, 255, 249, 254 + ], + [248, 196, 206, 252, 212, 255, 249, 254], [249, 197, 207, 253, 213, 255, 249, 254], [249, 197, 207, 253, 213, 255, 249, 254 + ], + [249, 198, 208, 253, 214, 255, 249, 254], [249, 199, 209, 253, 214, 255, 249, 254], [250, 200, 210, 253, 215, 255, 249, 254 + ], + [250, 200, 210, 253, 215, 255, 249, 254], [250, 201, 211, 253, 215, 255, 249, 254], [250, 202, 211, 253, 215, 255, 249, 254 + ], + [250, 203, 212, 253, 216, 255, 249, 254], [250, 203, 212, 253, 216, 255, 249, 254], [251, 204, 213, 253, 217, 255, 250, 254 + ], + [251, 205, 213, 253, 217, 255, 250, 254], [251, 206, 214, 254, 218, 255, 250, 254], [251, 206, 215, 254, 218, 255, 250, 254 + ], + [252, 207, 216, 254, 219, 255, 250, 254], [252, 208, 216, 254, 219, 255, 250, 254], [252, 209, 217, 254, 220, 255, 250, 254 + ], + [252, 210, 217, 254, 220, 255, 250, 254], [252, 211, 218, 254, 221, 255, 250, 254], [252, 212, 218, 254, 221, 255, 250, 254 + ], + [253, 213, 219, 254, 222, 255, 250, 254], [253, 213, 220, 254, 222, 255, 250, 254], [253, 214, 221, 254, 223, 255, 250, 254 + ], + [253, 215, 221, 254, 223, 255, 250, 254], [253, 216, 222, 254, 224, 255, 251, 254], [253, 217, 223, 254, 224, 255, 251, 254 + ], + [253, 218, 224, 254, 225, 255, 251, 254], [253, 219, 224, 254, 225, 255, 251, 254], [254, 220, 225, 254, 225, 255, 251, 254 + ], + [254, 221, 226, 254, 225, 255, 251, 254], [254, 222, 227, 255, 226, 255, 251, 254], [254, 223, 227, 255, 226, 255, 251, 254 + ], + [254, 224, 228, 255, 227, 255, 251, 254], [254, 225, 229, 255, 227, 255, 251, 254], [254, 226, 230, 255, 228, 255, 251, 254 + ], + [254, 227, 230, 255, 229, 255, 251, 254], [255, 228, 231, 255, 230, 255, 251, 254], [255, 229, 232, 255, 230, 255, 251, 254 + ], + [255, 230, 233, 255, 231, 255, 252, 254], [255, 231, 234, 255, 231, 255, 252, 254], [255, 232, 235, 255, 232, 255, 252, 254 + ], + [255, 233, 236, 255, 232, 255, 252, 254], [255, 235, 237, 255, 233, 255, 252, 254], [255, 236, 238, 255, 234, 255, 252, 254 + ], + [255, 238, 240, 255, 235, 255, 252, 255], [255, 239, 241, 255, 235, 255, 252, 254], [255, 241, 243, 255, 236, 255, 252, 254 + ], + [255, 243, 245, 255, 237, 255, 252, 254], [255, 246, 247, 255, 239, 255, 253, 255], }; - // Array indices are identical to previously-existing INTRAMODECONTEXTNODES. - public static readonly sbyte[] Vp9IntraModeTree = { - -(sbyte)PredictionMode.DcPred, 2, // 0 = DC_NODE - -(sbyte)PredictionMode.TmPred, 4, // 1 = TM_NODE - -(sbyte)PredictionMode.VPred, 6, // 2 = V_NODE - 8, 12, // 3 = COM_NODE - -(sbyte)PredictionMode.HPred, 10, // 4 = H_NODE - -(sbyte)PredictionMode.D135Pred, -(sbyte)PredictionMode.D117Pred, // 5 = D135_NODE - -(sbyte)PredictionMode.D45Pred, 14, // 6 = D45_NODE - -(sbyte)PredictionMode.D63Pred, 16, // 7 = D63_NODE - -(sbyte)PredictionMode.D153Pred, -(sbyte)PredictionMode.D207Pred, // 8 = D153_NODE - }; + /* Array indices are identical to previously-existing INTRAMODECONTEXTNODES. */ + public static readonly sbyte[] IntraModeTree = + [ + -(sbyte)PredictionMode.DcPred, 2, /* 0 = DC_NODE */ + -(sbyte)PredictionMode.TmPred, 4, /* 1 = TM_NODE */ + -(sbyte)PredictionMode.VPred, 6, /* 2 = V_NODE */ + 8, 12, /* 3 = COM_NODE */ + -(sbyte)PredictionMode.HPred, 10, /* 4 = H_NODE */ + -(sbyte)PredictionMode.D135Pred, -(sbyte)PredictionMode.D117Pred, /* 5 = D135_NODE */ + -(sbyte)PredictionMode.D45Pred, 14, /* 6 = D45_NODE */ + -(sbyte)PredictionMode.D63Pred, 16, /* 7 = D63_NODE */ + -(sbyte)PredictionMode.D153Pred, -(sbyte)PredictionMode.D207Pred /* 8 = D153_NODE */ + ]; - public static readonly sbyte[] Vp9InterModeTree = { + public static readonly sbyte[] InterModeTree = + [ -((sbyte)PredictionMode.ZeroMv - (sbyte)PredictionMode. NearestMv), 2, -((sbyte)PredictionMode.NearestMv - (sbyte)PredictionMode.NearestMv), 4, -((sbyte)PredictionMode.NearMv - (sbyte)PredictionMode.NearestMv), - -((sbyte)PredictionMode.NewMv - (sbyte)PredictionMode.NearestMv), - }; + -((sbyte)PredictionMode.NewMv - (sbyte)PredictionMode.NearestMv) + ]; - public static readonly sbyte[] Vp9PartitionTree = { - -(sbyte)PartitionType.PartitionNone, 2, -(sbyte)PartitionType.PartitionHorz, 4, -(sbyte)PartitionType.PartitionVert, -(sbyte)PartitionType.PartitionSplit, - }; + public static readonly sbyte[] PartitionTree = + [ + -(sbyte)PartitionType.PartitionNone, 2, -(sbyte)PartitionType.PartitionHorz, 4, -(sbyte)PartitionType.PartitionVert, -(sbyte)PartitionType.PartitionSplit + ]; - public static readonly sbyte[] Vp9SwitchableInterpTree = { - -Constants.EightTap, 2, -Constants.EightTapSmooth, -Constants.EightTapSharp, - }; + public static readonly sbyte[] SwitchableInterpTree = + [ + -Constants.EightTap, 2, -Constants.EightTapSmooth, -Constants.EightTapSharp + ]; - public static readonly sbyte[] Vp9SegmentTree = { - 2, 4, 6, 8, 10, 12, 0, -1, -2, -3, -4, -5, -6, -7, - }; + public static readonly sbyte[] SegmentTree = + [ + 2, 4, 6, 8, 10, 12, 0, -1, -2, -3, -4, -5, -6, -7 + ]; // MV Ref @@ -1390,192 +1357,195 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 // adding 9 for each intra block, 3 for each zero mv and 1 for each new // motion vector. This single number is then converted into a context // with a single lookup ( CounterToContext ). - public static readonly int[] Mode2Counter = { - 9, // DC_PRED - 9, // V_PRED - 9, // H_PRED - 9, // D45_PRED - 9, // D135_PRED - 9, // D117_PRED - 9, // D153_PRED - 9, // D207_PRED - 9, // D63_PRED - 9, // TM_PRED - 0, // NEARESTMV - 0, // NEARMV - 3, // ZEROMV - 1, // NEWMV - }; + public static readonly int[] Mode2Counter = + [ + 9, // DC_PRED + 9, // V_PRED + 9, // H_PRED + 9, // D45_PRED + 9, // D135_PRED + 9, // D117_PRED + 9, // D153_PRED + 9, // D207_PRED + 9, // D63_PRED + 9, // TM_PRED + 0, // NEARESTMV + 0, // NEARMV + 3, // ZEROMV + 1 // NEWMV + ]; // There are 3^3 different combinations of 3 counts that can be either 0,1 or // 2. However the actual count can never be greater than 2 so the highest // counter we need is 18. 9 is an invalid counter that's never used. - public static readonly MotionVectorContext[] CounterToContext = { - MotionVectorContext.BothPredicted, // 0 - MotionVectorContext.NewPlusNonIntra, // 1 - MotionVectorContext.BothNew, // 2 + public static readonly MotionVectorContext[] CounterToContext = + [ + MotionVectorContext.BothPredicted, // 0 + MotionVectorContext.NewPlusNonIntra, // 1 + MotionVectorContext.BothNew, // 2 MotionVectorContext.ZeroPlusPredicted, // 3 - MotionVectorContext.NewPlusNonIntra, // 4 - MotionVectorContext.InvalidCase, // 5 - MotionVectorContext.BothZero, // 6 - MotionVectorContext.InvalidCase, // 7 - MotionVectorContext.InvalidCase, // 8 + MotionVectorContext.NewPlusNonIntra, // 4 + MotionVectorContext.InvalidCase, // 5 + MotionVectorContext.BothZero, // 6 + MotionVectorContext.InvalidCase, // 7 + MotionVectorContext.InvalidCase, // 8 MotionVectorContext.IntraPlusNonIntra, // 9 MotionVectorContext.IntraPlusNonIntra, // 10 - MotionVectorContext.InvalidCase, // 11 + MotionVectorContext.InvalidCase, // 11 MotionVectorContext.IntraPlusNonIntra, // 12 - MotionVectorContext.InvalidCase, // 13 - MotionVectorContext.InvalidCase, // 14 - MotionVectorContext.InvalidCase, // 15 - MotionVectorContext.InvalidCase, // 16 - MotionVectorContext.InvalidCase, // 17 - MotionVectorContext.BothIntra, // 18 - }; + MotionVectorContext.InvalidCase, // 13 + MotionVectorContext.InvalidCase, // 14 + MotionVectorContext.InvalidCase, // 15 + MotionVectorContext.InvalidCase, // 16 + MotionVectorContext.InvalidCase, // 17 + MotionVectorContext.BothIntra // 18 + ]; - public static readonly Position[][] MvRefBlocks = { + public static readonly Position[][] MvRefBlocks = new Position[][] + { // 4X4 - new Position[] { - new(-1, 0), - new(0, -1), - new(-1, -1), - new(-2, 0), - new(0, -2), - new(-2, -1), - new(-1, -2), - new(-2, -2), - }, - // 4X8 - new Position[] { - new(-1, 0), - new(0, -1), - new(-1, -1), - new(-2, 0), - new(0, -2), - new(-2, -1), - new(-1, -2), - new(-2, -2), - }, - // 8X4 - new Position[] { - new(-1, 0), - new(0, -1), - new(-1, -1), - new(-2, 0), - new(0, -2), - new(-2, -1), - new(-1, -2), - new(-2, -2), - }, + [ + new Position( -1, 0 ), + new Position( 0, -1 ), + new Position( -1, -1 ), + new Position( -2, 0 ), + new Position( 0, -2 ), + new Position( -2, -1 ), + new Position( -1, -2 ), + new Position( -2, -2 ) + ], + // 4x8 + [ + new Position( -1, 0 ), + new Position( 0, -1 ), + new Position( -1, -1 ), + new Position( -2, 0 ), + new Position( 0, -2 ), + new Position( -2, -1 ), + new Position( -1, -2 ), + new Position( -2, -2 ) + ], + // 8x4 + [ + new Position( -1, 0 ), + new Position( 0, -1 ), + new Position( -1, -1 ), + new Position( -2, 0 ), + new Position( 0, -2 ), + new Position( -2, -1 ), + new Position( -1, -2 ), + new Position( -2, -2 ) + ], // 8X8 - new Position[] { - new(-1, 0), - new(0, -1), - new(-1, -1), - new(-2, 0), - new(0, -2), - new(-2, -1), - new(-1, -2), - new(-2, -2), - }, - // 8X16 - new Position[] { - new(0, -1), - new(-1, 0), - new(1, -1), - new(-1, -1), - new(0, -2), - new(-2, 0), - new(-2, -1), - new(-1, -2), - }, - // 16X8 - new Position[] { - new(-1, 0), - new(0, -1), - new(-1, 1), - new(-1, -1), - new(-2, 0), - new(0, -2), - new(-1, -2), - new(-2, -1), - }, + [ + new Position( -1, 0 ), + new Position( 0, -1 ), + new Position( -1, -1 ), + new Position( -2, 0 ), + new Position( 0, -2 ), + new Position( -2, -1 ), + new Position( -1, -2 ), + new Position( -2, -2 ) + ], + // 8x16 + [ + new Position( 0, -1 ), + new Position( -1, 0 ), + new Position( 1, -1 ), + new Position( -1, -1 ), + new Position( 0, -2 ), + new Position( -2, 0 ), + new Position( -2, -1 ), + new Position( -1, -2 ) + ], + // 16x8 + [ + new Position( -1, 0 ), + new Position( 0, -1 ), + new Position( -1, 1 ), + new Position( -1, -1 ), + new Position( -2, 0 ), + new Position( 0, -2 ), + new Position( -1, -2 ), + new Position( -2, -1 ) + ], // 16X16 - new Position[] { - new(-1, 0), - new(0, -1), - new(-1, 1), - new(1, -1), - new(-1, -1), - new(-3, 0), - new(0, -3), - new(-3, -3), - }, - // 16X32 - new Position[] { - new(0, -1), - new(-1, 0), - new(2, -1), - new(-1, -1), - new(-1, 1), - new(0, -3), - new(-3, 0), - new(-3, -3), - }, - // 32X16 - new Position[] { - new(-1, 0), - new(0, -1), - new(-1, 2), - new(-1, -1), - new(1, -1), - new(-3, 0), - new(0, -3), - new(-3, -3), - }, + [ + new Position( -1, 0 ), + new Position( 0, -1 ), + new Position( -1, 1 ), + new Position( 1, -1 ), + new Position( -1, -1 ), + new Position( -3, 0 ), + new Position( 0, -3 ), + new Position( -3, -3 ) + ], + // 16x32 + [ + new Position( 0, -1 ), + new Position( -1, 0 ), + new Position( 2, -1 ), + new Position( -1, -1 ), + new Position( -1, 1 ), + new Position( 0, -3 ), + new Position( -3, 0 ), + new Position( -3, -3 ) + ], + // 32x16 + [ + new Position( -1, 0 ), + new Position( 0, -1 ), + new Position( -1, 2 ), + new Position( -1, -1 ), + new Position( 1, -1 ), + new Position( -3, 0 ), + new Position( 0, -3 ), + new Position( -3, -3 ) + ], // 32X32 - new Position[] { - new(-1, 1), - new(1, -1), - new(-1, 2), - new(2, -1), - new(-1, -1), - new(-3, 0), - new(0, -3), - new(-3, -3), - }, - // 32X64 - new Position[] { - new(0, -1), - new(-1, 0), - new(4, -1), - new(-1, 2), - new(-1, -1), - new(0, -3), - new(-3, 0), - new(2, -1), - }, - // 64X32 - new Position[] { - new(-1, 0), - new(0, -1), - new(-1, 4), - new(2, -1), - new(-1, -1), - new(-3, 0), - new(0, -3), - new(-1, 2), - }, - // 64X64 - new Position[] { - new(-1, 3), - new(3, -1), - new(-1, 4), - new(4, -1), - new(-1, -1), - new(-1, 0), - new(0, -1), - new(-1, 6), - }, + [ + new Position( -1, 1 ), + new Position( 1, -1 ), + new Position( -1, 2 ), + new Position( 2, -1 ), + new Position( -1, -1 ), + new Position( -3, 0 ), + new Position( 0, -3 ), + new Position( -3, -3 ) + ], + // 32x64 + [ + new Position( 0, -1 ), + new Position( -1, 0 ), + new Position( 4, -1 ), + new Position( -1, 2 ), + new Position( -1, -1 ), + new Position( 0, -3 ), + new Position( -3, 0 ), + new Position( 2, -1 ) + ], + // 64x32 + [ + new Position( -1, 0 ), + new Position( 0, -1 ), + new Position( -1, 4 ), + new Position( 2, -1 ), + new Position( -1, -1 ), + new Position( -3, 0 ), + new Position( 0, -3 ), + new Position( -1, 2 ) + ], + // 64x64 + [ + new Position( -1, 3 ), + new Position( 3, -1 ), + new Position( -1, 4 ), + new Position( 4, -1 ), + new Position( -1, -1 ), + new Position( -1, 0 ), + new Position( 0, -1 ), + new Position( -1, 6 ) + ] }; } } diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/PredCommon.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/PredCommon.cs index 1f391f6eb..30cab3b60 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/PredCommon.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/PredCommon.cs @@ -1,4 +1,4 @@ -using Ryujinx.Graphics.Nvdec.Vp9.Types; +using Ryujinx.Graphics.Nvdec.Vp9.Types; using System.Diagnostics; namespace Ryujinx.Graphics.Nvdec.Vp9 @@ -13,7 +13,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 // left of the entries corresponding to real macroblocks. // The prediction flags in these dummy entries are initialized to 0. if (!xd.AboveMi.IsNull && !xd.LeftMi.IsNull) - { // both edges available + { + // both edges available if (!xd.AboveMi.Value.HasSecondRef() && !xd.LeftMi.Value.HasSecondRef()) { // Neither edge uses comp pred (0/1) @@ -23,12 +24,15 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 else if (!xd.AboveMi.Value.HasSecondRef()) { // One of two edges uses comp pred (2/3) - ctx = 2 + (xd.AboveMi.Value.RefFrame[0] == cm.CompFixedRef || !xd.AboveMi.Value.IsInterBlock() ? 1 : 0); + ctx = 2 + (xd.AboveMi.Value.RefFrame[0] == cm.CompFixedRef || !xd.AboveMi.Value.IsInterBlock() + ? 1 + : 0); } else if (!xd.LeftMi.Value.HasSecondRef()) { // One of two edges uses comp pred (2/3) - ctx = 2 + (xd.LeftMi.Value.RefFrame[0] == cm.CompFixedRef || !xd.LeftMi.Value.IsInterBlock() ? 1 : 0); + ctx = 2 + + (xd.LeftMi.Value.RefFrame[0] == cm.CompFixedRef || !xd.LeftMi.Value.IsInterBlock() ? 1 : 0); } else // Both edges use comp pred (4) { @@ -36,7 +40,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 } } else if (!xd.AboveMi.IsNull || !xd.LeftMi.IsNull) - { // One edge available + { + // One edge available ref ModeInfo edgeMi = ref !xd.AboveMi.IsNull ? ref xd.AboveMi.Value : ref xd.LeftMi.Value; if (!edgeMi.HasSecondRef()) @@ -51,11 +56,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 } } else - { // No edges available (1) + { + // No edges available (1) ctx = 1; } - Debug.Assert(ctx >= 0 && ctx < Constants.CompInterContexts); + Debug.Assert(ctx >= 0 && ctx < Constants.CompInterContexts); return ctx; } @@ -71,29 +77,33 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 int varRefIdx = fixRefIdx == 0 ? 1 : 0; if (!xd.AboveMi.IsNull && !xd.LeftMi.IsNull) - { // Both edges available + { + // Both edges available bool aboveIntra = !xd.AboveMi.Value.IsInterBlock(); bool leftIntra = !xd.LeftMi.Value.IsInterBlock(); if (aboveIntra && leftIntra) - { // Intra/Intra (2) + { + // Intra/Intra (2) predContext = 2; } else if (aboveIntra || leftIntra) - { // Intra/Inter + { + // Intra/Inter ref ModeInfo edgeMi = ref aboveIntra ? ref xd.LeftMi.Value : ref xd.AboveMi.Value; if (!edgeMi.HasSecondRef()) // single pred (1/3) { - predContext = 1 + 2 * (edgeMi.RefFrame[0] != cm.CompVarRef[1] ? 1 : 0); + predContext = 1 + (2 * (edgeMi.RefFrame[0] != cm.CompVarRef[1] ? 1 : 0)); } else // Comp pred (1/3) { - predContext = 1 + 2 * (edgeMi.RefFrame[varRefIdx] != cm.CompVarRef[1] ? 1 : 0); + predContext = 1 + (2 * (edgeMi.RefFrame[varRefIdx] != cm.CompVarRef[1] ? 1 : 0)); } } else - { // Inter/Inter + { + // Inter/Inter bool lSg = !xd.LeftMi.Value.HasSecondRef(); bool aSg = !xd.AboveMi.Value.HasSecondRef(); sbyte vrfa = aSg ? xd.AboveMi.Value.RefFrame[0] : xd.AboveMi.Value.RefFrame[varRefIdx]; @@ -104,7 +114,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 predContext = 0; } else if (lSg && aSg) - { // Single/Single + { + // Single/Single if ((vrfa == cm.CompFixedRef && vrfl == cm.CompVarRef[0]) || (vrfl == cm.CompFixedRef && vrfa == cm.CompVarRef[0])) { @@ -120,7 +131,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 } } else if (lSg || aSg) - { // Single/Comp + { + // Single/Comp sbyte vrfc = lSg ? vrfa : vrfl; sbyte rfs = aSg ? vrfa : vrfl; if (vrfc == cm.CompVarRef[1] && rfs != cm.CompVarRef[1]) @@ -137,7 +149,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 } } else if (vrfa == vrfl) - { // Comp/Comp + { + // Comp/Comp predContext = 4; } else @@ -147,7 +160,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 } } else if (!xd.AboveMi.IsNull || !xd.LeftMi.IsNull) - { // One edge available + { + // One edge available ref ModeInfo edgeMi = ref !xd.AboveMi.IsNull ? ref xd.AboveMi.Value : ref xd.LeftMi.Value; if (!edgeMi.IsInterBlock()) @@ -167,11 +181,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 } } else - { // No edges available (2) + { + // No edges available (2) predContext = 2; } - Debug.Assert(predContext >= 0 && predContext < Constants.RefContexts); + Debug.Assert(predContext >= 0 && predContext < Constants.RefContexts); return predContext; } @@ -183,16 +198,19 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 // left of the entries corresponding to real macroblocks. // The prediction flags in these dummy entries are initialized to 0. if (!xd.AboveMi.IsNull && !xd.LeftMi.IsNull) - { // Both edges available + { + // Both edges available bool aboveIntra = !xd.AboveMi.Value.IsInterBlock(); bool leftIntra = !xd.LeftMi.Value.IsInterBlock(); if (aboveIntra && leftIntra) - { // Intra/Intra + { + // Intra/Intra predContext = 2; } else if (aboveIntra || leftIntra) - { // Intra/Inter or Inter/Intra + { + // Intra/Inter or Inter/Intra ref ModeInfo edgeMi = ref aboveIntra ? ref xd.LeftMi.Value : ref xd.AboveMi.Value; if (!edgeMi.HasSecondRef()) { @@ -201,11 +219,14 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 else { predContext = 1 + (edgeMi.RefFrame[0] == Constants.LastFrame || - edgeMi.RefFrame[1] == Constants.LastFrame ? 1 : 0); + edgeMi.RefFrame[1] == Constants.LastFrame + ? 1 + : 0); } } else - { // Inter/Inter + { + // Inter/Inter bool aboveHasSecond = xd.AboveMi.Value.HasSecondRef(); bool leftHasSecond = xd.LeftMi.Value.HasSecondRef(); sbyte above0 = xd.AboveMi.Value.RefFrame[0]; @@ -216,7 +237,9 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 if (aboveHasSecond && leftHasSecond) { predContext = 1 + (above0 == Constants.LastFrame || above1 == Constants.LastFrame || - left0 == Constants.LastFrame || left1 == Constants.LastFrame ? 1 : 0); + left0 == Constants.LastFrame || left1 == Constants.LastFrame + ? 1 + : 0); } else if (aboveHasSecond || leftHasSecond) { @@ -230,24 +253,28 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 } else { - predContext = (crf1 == Constants.LastFrame || crf2 == Constants.LastFrame ? 1 : 0); + predContext = crf1 == Constants.LastFrame || crf2 == Constants.LastFrame ? 1 : 0; } } else { - predContext = 2 * (above0 == Constants.LastFrame ? 1 : 0) + 2 * (left0 == Constants.LastFrame ? 1 : 0); + predContext = (2 * (above0 == Constants.LastFrame ? 1 : 0)) + + (2 * (left0 == Constants.LastFrame ? 1 : 0)); } } } else if (!xd.AboveMi.IsNull || !xd.LeftMi.IsNull) - { // One edge available + { + // One edge available ref ModeInfo edgeMi = ref !xd.AboveMi.IsNull ? ref xd.AboveMi.Value : ref xd.LeftMi.Value; if (!edgeMi.IsInterBlock()) - { // Intra + { + // Intra predContext = 2; } else - { // Inter + { + // Inter if (!edgeMi.HasSecondRef()) { predContext = 4 * (edgeMi.RefFrame[0] == Constants.LastFrame ? 1 : 0); @@ -255,16 +282,19 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 else { predContext = 1 + (edgeMi.RefFrame[0] == Constants.LastFrame || - edgeMi.RefFrame[1] == Constants.LastFrame ? 1 : 0); + edgeMi.RefFrame[1] == Constants.LastFrame + ? 1 + : 0); } } } else - { // No edges available + { + // No edges available predContext = 2; } - Debug.Assert(predContext >= 0 && predContext < Constants.RefContexts); + Debug.Assert(predContext >= 0 && predContext < Constants.RefContexts); return predContext; } @@ -277,16 +307,19 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 // left of the entries corresponding to real macroblocks. // The prediction flags in these dummy entries are initialized to 0. if (!xd.AboveMi.IsNull && !xd.LeftMi.IsNull) - { // Both edges available + { + // Both edges available bool aboveIntra = !xd.AboveMi.Value.IsInterBlock(); bool leftIntra = !xd.LeftMi.Value.IsInterBlock(); if (aboveIntra && leftIntra) - { // Intra/Intra + { + // Intra/Intra predContext = 2; } else if (aboveIntra || leftIntra) - { // Intra/Inter or Inter/Intra + { + // Intra/Inter or Inter/Intra ref ModeInfo edgeMi = ref aboveIntra ? ref xd.LeftMi.Value : ref xd.AboveMi.Value; if (!edgeMi.HasSecondRef()) { @@ -301,12 +334,15 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 } else { - predContext = 1 + 2 * (edgeMi.RefFrame[0] == Constants.GoldenFrame || - edgeMi.RefFrame[1] == Constants.GoldenFrame ? 1 : 0); + predContext = 1 + (2 * (edgeMi.RefFrame[0] == Constants.GoldenFrame || + edgeMi.RefFrame[1] == Constants.GoldenFrame + ? 1 + : 0)); } } else - { // Inter/Inter + { + // Inter/Inter bool aboveHasSecond = xd.AboveMi.Value.HasSecondRef(); bool leftHasSecond = xd.LeftMi.Value.HasSecondRef(); sbyte above0 = xd.AboveMi.Value.RefFrame[0]; @@ -319,7 +355,9 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 if (above0 == left0 && above1 == left1) { predContext = 3 * (above0 == Constants.GoldenFrame || above1 == Constants.GoldenFrame || - left0 == Constants.GoldenFrame || left1 == Constants.GoldenFrame ? 1 : 0); + left0 == Constants.GoldenFrame || left1 == Constants.GoldenFrame + ? 1 + : 0); } else { @@ -342,7 +380,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 } else { - predContext = 1 + 2 * (crf1 == Constants.GoldenFrame || crf2 == Constants.GoldenFrame ? 1 : 0); + predContext = + 1 + (2 * (crf1 == Constants.GoldenFrame || crf2 == Constants.GoldenFrame ? 1 : 0)); } } else @@ -353,18 +392,20 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 } else if (above0 == Constants.LastFrame || left0 == Constants.LastFrame) { - sbyte edge0 = (above0 == Constants.LastFrame) ? left0 : above0; + sbyte edge0 = above0 == Constants.LastFrame ? left0 : above0; predContext = 4 * (edge0 == Constants.GoldenFrame ? 1 : 0); } else { - predContext = 2 * (above0 == Constants.GoldenFrame ? 1 : 0) + 2 * (left0 == Constants.GoldenFrame ? 1 : 0); + predContext = (2 * (above0 == Constants.GoldenFrame ? 1 : 0)) + + (2 * (left0 == Constants.GoldenFrame ? 1 : 0)); } } } } else if (!xd.AboveMi.IsNull || !xd.LeftMi.IsNull) - { // One edge available + { + // One edge available ref ModeInfo edgeMi = ref !xd.AboveMi.IsNull ? ref xd.AboveMi.Value : ref xd.LeftMi.Value; if (!edgeMi.IsInterBlock() || (edgeMi.RefFrame[0] == Constants.LastFrame && !edgeMi.HasSecondRef())) @@ -378,16 +419,19 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 else { predContext = 3 * (edgeMi.RefFrame[0] == Constants.GoldenFrame || - edgeMi.RefFrame[1] == Constants.GoldenFrame ? 1 : 0); + edgeMi.RefFrame[1] == Constants.GoldenFrame + ? 1 + : 0); } } else - { // No edges available (2) + { + // No edges available (2) predContext = 2; } - Debug.Assert(predContext >= 0 && predContext < Constants.RefContexts); + Debug.Assert(predContext >= 0 && predContext < Constants.RefContexts); return predContext; } } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Prob.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Prob.cs new file mode 100644 index 000000000..ca6fa351a --- /dev/null +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Prob.cs @@ -0,0 +1,94 @@ +using Ryujinx.Common.Memory; +using Ryujinx.Graphics.Nvdec.Vp9.Common; +using System; +using System.Diagnostics; + +namespace Ryujinx.Graphics.Nvdec.Vp9 +{ + public static class Prob + { + public const int MaxProb = 255; + + private static byte GetProb(uint num, uint den) + { + Debug.Assert(den != 0); + { + int p = (int)((((ulong)num * 256) + (den >> 1)) / den); + // (p > 255) ? 255 : (p < 1) ? 1 : p; + int clippedProb = p | ((255 - p) >> 23) | (p == 0 ? 1 : 0); + return (byte)clippedProb; + } + } + + private static byte GetBinaryProb(uint n0, uint n1) + { + uint den = n0 + n1; + if (den == 0) + { + return 128; + } + + return GetProb(n0, den); + } + + /* This function assumes prob1 and prob2 are already within [1,255] range. */ + public static byte WeightedProb(int prob1, int prob2, int factor) + { + return (byte)BitUtils.RoundPowerOfTwo((prob1 * (256 - factor)) + (prob2 * factor), 8); + } + + public static byte MergeProbs(byte preProb, ref Array2 ct, uint countSat, uint maxUpdateFactor) + { + byte prob = GetBinaryProb(ct[0], ct[1]); + uint count = Math.Min(ct[0] + ct[1], countSat); + uint factor = maxUpdateFactor * count / countSat; + return WeightedProb(preProb, prob, (int)factor); + } + + // MODE_MV_MAX_UPDATE_FACTOR (128) * count / MODE_MV_COUNT_SAT; + private static readonly uint[] _countToUpdateFactor = + [ + 0, 6, 12, 19, 25, 32, 38, 44, 51, 57, 64, 70, 76, 83, 89, 96, 102, 108, 115, 121, 128 + ]; + + private const int ModeMvCountSat = 20; + + public static byte ModeMvMergeProbs(byte preProb, ref Array2 ct) + { + uint den = ct[0] + ct[1]; + if (den == 0) + { + return preProb; + } + + uint count = Math.Min(den, ModeMvCountSat); + uint factor = _countToUpdateFactor[(int)count]; + byte prob = GetProb(ct[0], den); + return WeightedProb(preProb, prob, (int)factor); + } + + private static uint TreeMergeProbsImpl( + uint i, + sbyte[] tree, + ReadOnlySpan preProbs, + ReadOnlySpan counts, + Span probs) + { + int l = tree[i]; + uint leftCount = l <= 0 ? counts[-l] : TreeMergeProbsImpl((uint)l, tree, preProbs, counts, probs); + int r = tree[i + 1]; + uint rightCount = r <= 0 ? counts[-r] : TreeMergeProbsImpl((uint)r, tree, preProbs, counts, probs); + Array2 ct = new(); + ct[0] = leftCount; + ct[1] = rightCount; + probs[(int)(i >> 1)] = ModeMvMergeProbs(preProbs[(int)(i >> 1)], ref ct); + return leftCount + rightCount; + } + + public static void VpxTreeMergeProbs(sbyte[] tree, ReadOnlySpan preProbs, ReadOnlySpan counts, + Span probs) + { + TreeMergeProbsImpl(0, tree, preProbs, counts, probs); + } + } +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/QuantCommon.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/QuantCommon.cs index 8e8fafdb1..3542c90c4 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/QuantCommon.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/QuantCommon.cs @@ -1,172 +1,127 @@ -using Ryujinx.Graphics.Nvdec.Vp9.Types; -using System; +using System; using System.Diagnostics; namespace Ryujinx.Graphics.Nvdec.Vp9 { internal static class QuantCommon { - public const int MinQ = 0; public const int MaxQ = 255; + public const int QindexBits = 8; - private static readonly short[] _dcQlookup = { - 4, 8, 8, 9, 10, 11, 12, 12, 13, 14, 15, 16, 17, 18, - 19, 19, 20, 21, 22, 23, 24, 25, 26, 26, 27, 28, 29, 30, - 31, 32, 32, 33, 34, 35, 36, 37, 38, 38, 39, 40, 41, 42, - 43, 43, 44, 45, 46, 47, 48, 48, 49, 50, 51, 52, 53, 53, - 54, 55, 56, 57, 57, 58, 59, 60, 61, 62, 62, 63, 64, 65, - 66, 66, 67, 68, 69, 70, 70, 71, 72, 73, 74, 74, 75, 76, - 77, 78, 78, 79, 80, 81, 81, 82, 83, 84, 85, 85, 87, 88, - 90, 92, 93, 95, 96, 98, 99, 101, 102, 104, 105, 107, 108, 110, - 111, 113, 114, 116, 117, 118, 120, 121, 123, 125, 127, 129, 131, 134, - 136, 138, 140, 142, 144, 146, 148, 150, 152, 154, 156, 158, 161, 164, - 166, 169, 172, 174, 177, 180, 182, 185, 187, 190, 192, 195, 199, 202, - 205, 208, 211, 214, 217, 220, 223, 226, 230, 233, 237, 240, 243, 247, - 250, 253, 257, 261, 265, 269, 272, 276, 280, 284, 288, 292, 296, 300, - 304, 309, 313, 317, 322, 326, 330, 335, 340, 344, 349, 354, 359, 364, - 369, 374, 379, 384, 389, 395, 400, 406, 411, 417, 423, 429, 435, 441, - 447, 454, 461, 467, 475, 482, 489, 497, 505, 513, 522, 530, 539, 549, - 559, 569, 579, 590, 602, 614, 626, 640, 654, 668, 684, 700, 717, 736, - 755, 775, 796, 819, 843, 869, 896, 925, 955, 988, 1022, 1058, 1098, 1139, - 1184, 1232, 1282, 1336, - }; + private static readonly short[] _dcQlookup = + [ + 4, 8, 8, 9, 10, 11, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 23, 24, 25, 26, 26, 27, 28, 29, + 30, 31, 32, 32, 33, 34, 35, 36, 37, 38, 38, 39, 40, 41, 42, 43, 43, 44, 45, 46, 47, 48, 48, 49, 50, 51, + 52, 53, 53, 54, 55, 56, 57, 57, 58, 59, 60, 61, 62, 62, 63, 64, 65, 66, 66, 67, 68, 69, 70, 70, 71, 72, + 73, 74, 74, 75, 76, 77, 78, 78, 79, 80, 81, 81, 82, 83, 84, 85, 85, 87, 88, 90, 92, 93, 95, 96, 98, 99, + 101, 102, 104, 105, 107, 108, 110, 111, 113, 114, 116, 117, 118, 120, 121, 123, 125, 127, 129, 131, 134, + 136, 138, 140, 142, 144, 146, 148, 150, 152, 154, 156, 158, 161, 164, 166, 169, 172, 174, 177, 180, 182, + 185, 187, 190, 192, 195, 199, 202, 205, 208, 211, 214, 217, 220, 223, 226, 230, 233, 237, 240, 243, 247, + 250, 253, 257, 261, 265, 269, 272, 276, 280, 284, 288, 292, 296, 300, 304, 309, 313, 317, 322, 326, 330, + 335, 340, 344, 349, 354, 359, 364, 369, 374, 379, 384, 389, 395, 400, 406, 411, 417, 423, 429, 435, 441, + 447, 454, 461, 467, 475, 482, 489, 497, 505, 513, 522, 530, 539, 549, 559, 569, 579, 590, 602, 614, 626, + 640, 654, 668, 684, 700, 717, 736, 755, 775, 796, 819, 843, 869, 896, 925, 955, 988, 1022, 1058, 1098, + 1139, 1184, 1232, 1282, 1336 + ]; - private static readonly short[] _dcQlookup10 = { - 4, 9, 10, 13, 15, 17, 20, 22, 25, 28, 31, 34, 37, - 40, 43, 47, 50, 53, 57, 60, 64, 68, 71, 75, 78, 82, - 86, 90, 93, 97, 101, 105, 109, 113, 116, 120, 124, 128, 132, - 136, 140, 143, 147, 151, 155, 159, 163, 166, 170, 174, 178, 182, - 185, 189, 193, 197, 200, 204, 208, 212, 215, 219, 223, 226, 230, - 233, 237, 241, 244, 248, 251, 255, 259, 262, 266, 269, 273, 276, - 280, 283, 287, 290, 293, 297, 300, 304, 307, 310, 314, 317, 321, - 324, 327, 331, 334, 337, 343, 350, 356, 362, 369, 375, 381, 387, - 394, 400, 406, 412, 418, 424, 430, 436, 442, 448, 454, 460, 466, - 472, 478, 484, 490, 499, 507, 516, 525, 533, 542, 550, 559, 567, - 576, 584, 592, 601, 609, 617, 625, 634, 644, 655, 666, 676, 687, - 698, 708, 718, 729, 739, 749, 759, 770, 782, 795, 807, 819, 831, - 844, 856, 868, 880, 891, 906, 920, 933, 947, 961, 975, 988, 1001, - 1015, 1030, 1045, 1061, 1076, 1090, 1105, 1120, 1137, 1153, 1170, 1186, 1202, - 1218, 1236, 1253, 1271, 1288, 1306, 1323, 1342, 1361, 1379, 1398, 1416, 1436, - 1456, 1476, 1496, 1516, 1537, 1559, 1580, 1601, 1624, 1647, 1670, 1692, 1717, - 1741, 1766, 1791, 1817, 1844, 1871, 1900, 1929, 1958, 1990, 2021, 2054, 2088, - 2123, 2159, 2197, 2236, 2276, 2319, 2363, 2410, 2458, 2508, 2561, 2616, 2675, - 2737, 2802, 2871, 2944, 3020, 3102, 3188, 3280, 3375, 3478, 3586, 3702, 3823, - 3953, 4089, 4236, 4394, 4559, 4737, 4929, 5130, 5347, - }; + private static readonly short[] _dcQlookup10 = + [ + 4, 9, 10, 13, 15, 17, 20, 22, 25, 28, 31, 34, 37, 40, 43, 47, 50, 53, 57, 60, 64, 68, 71, 75, 78, 82, + 86, 90, 93, 97, 101, 105, 109, 113, 116, 120, 124, 128, 132, 136, 140, 143, 147, 151, 155, 159, 163, + 166, 170, 174, 178, 182, 185, 189, 193, 197, 200, 204, 208, 212, 215, 219, 223, 226, 230, 233, 237, 241, + 244, 248, 251, 255, 259, 262, 266, 269, 273, 276, 280, 283, 287, 290, 293, 297, 300, 304, 307, 310, 314, + 317, 321, 324, 327, 331, 334, 337, 343, 350, 356, 362, 369, 375, 381, 387, 394, 400, 406, 412, 418, 424, + 430, 436, 442, 448, 454, 460, 466, 472, 478, 484, 490, 499, 507, 516, 525, 533, 542, 550, 559, 567, 576, + 584, 592, 601, 609, 617, 625, 634, 644, 655, 666, 676, 687, 698, 708, 718, 729, 739, 749, 759, 770, 782, + 795, 807, 819, 831, 844, 856, 868, 880, 891, 906, 920, 933, 947, 961, 975, 988, 1001, 1015, 1030, 1045, + 1061, 1076, 1090, 1105, 1120, 1137, 1153, 1170, 1186, 1202, 1218, 1236, 1253, 1271, 1288, 1306, 1323, + 1342, 1361, 1379, 1398, 1416, 1436, 1456, 1476, 1496, 1516, 1537, 1559, 1580, 1601, 1624, 1647, 1670, + 1692, 1717, 1741, 1766, 1791, 1817, 1844, 1871, 1900, 1929, 1958, 1990, 2021, 2054, 2088, 2123, 2159, + 2197, 2236, 2276, 2319, 2363, 2410, 2458, 2508, 2561, 2616, 2675, 2737, 2802, 2871, 2944, 3020, 3102, + 3188, 3280, 3375, 3478, 3586, 3702, 3823, 3953, 4089, 4236, 4394, 4559, 4737, 4929, 5130, 5347 + ]; - private static readonly short[] _dcQlookup12 = { - 4, 12, 18, 25, 33, 41, 50, 60, 70, 80, 91, - 103, 115, 127, 140, 153, 166, 180, 194, 208, 222, 237, - 251, 266, 281, 296, 312, 327, 343, 358, 374, 390, 405, - 421, 437, 453, 469, 484, 500, 516, 532, 548, 564, 580, - 596, 611, 627, 643, 659, 674, 690, 706, 721, 737, 752, - 768, 783, 798, 814, 829, 844, 859, 874, 889, 904, 919, - 934, 949, 964, 978, 993, 1008, 1022, 1037, 1051, 1065, 1080, - 1094, 1108, 1122, 1136, 1151, 1165, 1179, 1192, 1206, 1220, 1234, - 1248, 1261, 1275, 1288, 1302, 1315, 1329, 1342, 1368, 1393, 1419, - 1444, 1469, 1494, 1519, 1544, 1569, 1594, 1618, 1643, 1668, 1692, - 1717, 1741, 1765, 1789, 1814, 1838, 1862, 1885, 1909, 1933, 1957, - 1992, 2027, 2061, 2096, 2130, 2165, 2199, 2233, 2267, 2300, 2334, - 2367, 2400, 2434, 2467, 2499, 2532, 2575, 2618, 2661, 2704, 2746, - 2788, 2830, 2872, 2913, 2954, 2995, 3036, 3076, 3127, 3177, 3226, - 3275, 3324, 3373, 3421, 3469, 3517, 3565, 3621, 3677, 3733, 3788, - 3843, 3897, 3951, 4005, 4058, 4119, 4181, 4241, 4301, 4361, 4420, - 4479, 4546, 4612, 4677, 4742, 4807, 4871, 4942, 5013, 5083, 5153, - 5222, 5291, 5367, 5442, 5517, 5591, 5665, 5745, 5825, 5905, 5984, - 6063, 6149, 6234, 6319, 6404, 6495, 6587, 6678, 6769, 6867, 6966, - 7064, 7163, 7269, 7376, 7483, 7599, 7715, 7832, 7958, 8085, 8214, - 8352, 8492, 8635, 8788, 8945, 9104, 9275, 9450, 9639, 9832, 10031, - 10245, 10465, 10702, 10946, 11210, 11482, 11776, 12081, 12409, 12750, 13118, - 13501, 13913, 14343, 14807, 15290, 15812, 16356, 16943, 17575, 18237, 18949, - 19718, 20521, 21387, - }; + private static readonly short[] _dcQlookup12 = + [ + 4, 12, 18, 25, 33, 41, 50, 60, 70, 80, 91, 103, 115, 127, 140, 153, 166, 180, 194, 208, 222, 237, 251, + 266, 281, 296, 312, 327, 343, 358, 374, 390, 405, 421, 437, 453, 469, 484, 500, 516, 532, 548, 564, 580, + 596, 611, 627, 643, 659, 674, 690, 706, 721, 737, 752, 768, 783, 798, 814, 829, 844, 859, 874, 889, 904, + 919, 934, 949, 964, 978, 993, 1008, 1022, 1037, 1051, 1065, 1080, 1094, 1108, 1122, 1136, 1151, 1165, + 1179, 1192, 1206, 1220, 1234, 1248, 1261, 1275, 1288, 1302, 1315, 1329, 1342, 1368, 1393, 1419, 1444, + 1469, 1494, 1519, 1544, 1569, 1594, 1618, 1643, 1668, 1692, 1717, 1741, 1765, 1789, 1814, 1838, 1862, + 1885, 1909, 1933, 1957, 1992, 2027, 2061, 2096, 2130, 2165, 2199, 2233, 2267, 2300, 2334, 2367, 2400, + 2434, 2467, 2499, 2532, 2575, 2618, 2661, 2704, 2746, 2788, 2830, 2872, 2913, 2954, 2995, 3036, 3076, + 3127, 3177, 3226, 3275, 3324, 3373, 3421, 3469, 3517, 3565, 3621, 3677, 3733, 3788, 3843, 3897, 3951, + 4005, 4058, 4119, 4181, 4241, 4301, 4361, 4420, 4479, 4546, 4612, 4677, 4742, 4807, 4871, 4942, 5013, + 5083, 5153, 5222, 5291, 5367, 5442, 5517, 5591, 5665, 5745, 5825, 5905, 5984, 6063, 6149, 6234, 6319, + 6404, 6495, 6587, 6678, 6769, 6867, 6966, 7064, 7163, 7269, 7376, 7483, 7599, 7715, 7832, 7958, 8085, + 8214, 8352, 8492, 8635, 8788, 8945, 9104, 9275, 9450, 9639, 9832, 10031, 10245, 10465, 10702, 10946, + 11210, 11482, 11776, 12081, 12409, 12750, 13118, 13501, 13913, 14343, 14807, 15290, 15812, 16356, 16943, + 17575, 18237, 18949, 19718, 20521, 21387 + ]; - private static readonly short[] _acQlookup = { - 4, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, - 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, - 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, - 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, - 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, - 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, - 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, - 98, 99, 100, 101, 102, 104, 106, 108, 110, 112, 114, 116, 118, - 120, 122, 124, 126, 128, 130, 132, 134, 136, 138, 140, 142, 144, - 146, 148, 150, 152, 155, 158, 161, 164, 167, 170, 173, 176, 179, - 182, 185, 188, 191, 194, 197, 200, 203, 207, 211, 215, 219, 223, - 227, 231, 235, 239, 243, 247, 251, 255, 260, 265, 270, 275, 280, - 285, 290, 295, 300, 305, 311, 317, 323, 329, 335, 341, 347, 353, - 359, 366, 373, 380, 387, 394, 401, 408, 416, 424, 432, 440, 448, - 456, 465, 474, 483, 492, 501, 510, 520, 530, 540, 550, 560, 571, - 582, 593, 604, 615, 627, 639, 651, 663, 676, 689, 702, 715, 729, - 743, 757, 771, 786, 801, 816, 832, 848, 864, 881, 898, 915, 933, - 951, 969, 988, 1007, 1026, 1046, 1066, 1087, 1108, 1129, 1151, 1173, 1196, - 1219, 1243, 1267, 1292, 1317, 1343, 1369, 1396, 1423, 1451, 1479, 1508, 1537, - 1567, 1597, 1628, 1660, 1692, 1725, 1759, 1793, 1828, - }; + private static readonly short[] _acQlookup = + [ + 4, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, + 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 104, 106, 108, 110, 112, 114, + 116, 118, 120, 122, 124, 126, 128, 130, 132, 134, 136, 138, 140, 142, 144, 146, 148, 150, 152, 155, 158, + 161, 164, 167, 170, 173, 176, 179, 182, 185, 188, 191, 194, 197, 200, 203, 207, 211, 215, 219, 223, 227, + 231, 235, 239, 243, 247, 251, 255, 260, 265, 270, 275, 280, 285, 290, 295, 300, 305, 311, 317, 323, 329, + 335, 341, 347, 353, 359, 366, 373, 380, 387, 394, 401, 408, 416, 424, 432, 440, 448, 456, 465, 474, 483, + 492, 501, 510, 520, 530, 540, 550, 560, 571, 582, 593, 604, 615, 627, 639, 651, 663, 676, 689, 702, 715, + 729, 743, 757, 771, 786, 801, 816, 832, 848, 864, 881, 898, 915, 933, 951, 969, 988, 1007, 1026, 1046, + 1066, 1087, 1108, 1129, 1151, 1173, 1196, 1219, 1243, 1267, 1292, 1317, 1343, 1369, 1396, 1423, 1451, + 1479, 1508, 1537, 1567, 1597, 1628, 1660, 1692, 1725, 1759, 1793, 1828 + ]; - private static readonly short[] _acQlookup10 = { - 4, 9, 11, 13, 16, 18, 21, 24, 27, 30, 33, 37, 40, - 44, 48, 51, 55, 59, 63, 67, 71, 75, 79, 83, 88, 92, - 96, 100, 105, 109, 114, 118, 122, 127, 131, 136, 140, 145, 149, - 154, 158, 163, 168, 172, 177, 181, 186, 190, 195, 199, 204, 208, - 213, 217, 222, 226, 231, 235, 240, 244, 249, 253, 258, 262, 267, - 271, 275, 280, 284, 289, 293, 297, 302, 306, 311, 315, 319, 324, - 328, 332, 337, 341, 345, 349, 354, 358, 362, 367, 371, 375, 379, - 384, 388, 392, 396, 401, 409, 417, 425, 433, 441, 449, 458, 466, - 474, 482, 490, 498, 506, 514, 523, 531, 539, 547, 555, 563, 571, - 579, 588, 596, 604, 616, 628, 640, 652, 664, 676, 688, 700, 713, - 725, 737, 749, 761, 773, 785, 797, 809, 825, 841, 857, 873, 889, - 905, 922, 938, 954, 970, 986, 1002, 1018, 1038, 1058, 1078, 1098, 1118, - 1138, 1158, 1178, 1198, 1218, 1242, 1266, 1290, 1314, 1338, 1362, 1386, 1411, - 1435, 1463, 1491, 1519, 1547, 1575, 1603, 1631, 1663, 1695, 1727, 1759, 1791, - 1823, 1859, 1895, 1931, 1967, 2003, 2039, 2079, 2119, 2159, 2199, 2239, 2283, - 2327, 2371, 2415, 2459, 2507, 2555, 2603, 2651, 2703, 2755, 2807, 2859, 2915, - 2971, 3027, 3083, 3143, 3203, 3263, 3327, 3391, 3455, 3523, 3591, 3659, 3731, - 3803, 3876, 3952, 4028, 4104, 4184, 4264, 4348, 4432, 4516, 4604, 4692, 4784, - 4876, 4972, 5068, 5168, 5268, 5372, 5476, 5584, 5692, 5804, 5916, 6032, 6148, - 6268, 6388, 6512, 6640, 6768, 6900, 7036, 7172, 7312, - }; + private static readonly short[] _acQlookup10 = + [ + 4, 9, 11, 13, 16, 18, 21, 24, 27, 30, 33, 37, 40, 44, 48, 51, 55, 59, 63, 67, 71, 75, 79, 83, 88, 92, + 96, 100, 105, 109, 114, 118, 122, 127, 131, 136, 140, 145, 149, 154, 158, 163, 168, 172, 177, 181, 186, + 190, 195, 199, 204, 208, 213, 217, 222, 226, 231, 235, 240, 244, 249, 253, 258, 262, 267, 271, 275, 280, + 284, 289, 293, 297, 302, 306, 311, 315, 319, 324, 328, 332, 337, 341, 345, 349, 354, 358, 362, 367, 371, + 375, 379, 384, 388, 392, 396, 401, 409, 417, 425, 433, 441, 449, 458, 466, 474, 482, 490, 498, 506, 514, + 523, 531, 539, 547, 555, 563, 571, 579, 588, 596, 604, 616, 628, 640, 652, 664, 676, 688, 700, 713, 725, + 737, 749, 761, 773, 785, 797, 809, 825, 841, 857, 873, 889, 905, 922, 938, 954, 970, 986, 1002, 1018, + 1038, 1058, 1078, 1098, 1118, 1138, 1158, 1178, 1198, 1218, 1242, 1266, 1290, 1314, 1338, 1362, 1386, + 1411, 1435, 1463, 1491, 1519, 1547, 1575, 1603, 1631, 1663, 1695, 1727, 1759, 1791, 1823, 1859, 1895, + 1931, 1967, 2003, 2039, 2079, 2119, 2159, 2199, 2239, 2283, 2327, 2371, 2415, 2459, 2507, 2555, 2603, + 2651, 2703, 2755, 2807, 2859, 2915, 2971, 3027, 3083, 3143, 3203, 3263, 3327, 3391, 3455, 3523, 3591, + 3659, 3731, 3803, 3876, 3952, 4028, 4104, 4184, 4264, 4348, 4432, 4516, 4604, 4692, 4784, 4876, 4972, + 5068, 5168, 5268, 5372, 5476, 5584, 5692, 5804, 5916, 6032, 6148, 6268, 6388, 6512, 6640, 6768, 6900, + 7036, 7172, 7312 + ]; - private static readonly short[] _acQlookup12 = { - 4, 13, 19, 27, 35, 44, 54, 64, 75, 87, 99, - 112, 126, 139, 154, 168, 183, 199, 214, 230, 247, 263, - 280, 297, 314, 331, 349, 366, 384, 402, 420, 438, 456, - 475, 493, 511, 530, 548, 567, 586, 604, 623, 642, 660, - 679, 698, 716, 735, 753, 772, 791, 809, 828, 846, 865, - 884, 902, 920, 939, 957, 976, 994, 1012, 1030, 1049, 1067, - 1085, 1103, 1121, 1139, 1157, 1175, 1193, 1211, 1229, 1246, 1264, - 1282, 1299, 1317, 1335, 1352, 1370, 1387, 1405, 1422, 1440, 1457, - 1474, 1491, 1509, 1526, 1543, 1560, 1577, 1595, 1627, 1660, 1693, - 1725, 1758, 1791, 1824, 1856, 1889, 1922, 1954, 1987, 2020, 2052, - 2085, 2118, 2150, 2183, 2216, 2248, 2281, 2313, 2346, 2378, 2411, - 2459, 2508, 2556, 2605, 2653, 2701, 2750, 2798, 2847, 2895, 2943, - 2992, 3040, 3088, 3137, 3185, 3234, 3298, 3362, 3426, 3491, 3555, - 3619, 3684, 3748, 3812, 3876, 3941, 4005, 4069, 4149, 4230, 4310, - 4390, 4470, 4550, 4631, 4711, 4791, 4871, 4967, 5064, 5160, 5256, - 5352, 5448, 5544, 5641, 5737, 5849, 5961, 6073, 6185, 6297, 6410, - 6522, 6650, 6778, 6906, 7034, 7162, 7290, 7435, 7579, 7723, 7867, - 8011, 8155, 8315, 8475, 8635, 8795, 8956, 9132, 9308, 9484, 9660, - 9836, 10028, 10220, 10412, 10604, 10812, 11020, 11228, 11437, 11661, 11885, - 12109, 12333, 12573, 12813, 13053, 13309, 13565, 13821, 14093, 14365, 14637, - 14925, 15213, 15502, 15806, 16110, 16414, 16734, 17054, 17390, 17726, 18062, - 18414, 18766, 19134, 19502, 19886, 20270, 20670, 21070, 21486, 21902, 22334, - 22766, 23214, 23662, 24126, 24590, 25070, 25551, 26047, 26559, 27071, 27599, - 28143, 28687, 29247, - }; + private static readonly short[] _acQlookup12 = + [ + 4, 13, 19, 27, 35, 44, 54, 64, 75, 87, 99, 112, 126, 139, 154, 168, 183, 199, 214, 230, 247, 263, 280, + 297, 314, 331, 349, 366, 384, 402, 420, 438, 456, 475, 493, 511, 530, 548, 567, 586, 604, 623, 642, 660, + 679, 698, 716, 735, 753, 772, 791, 809, 828, 846, 865, 884, 902, 920, 939, 957, 976, 994, 1012, 1030, + 1049, 1067, 1085, 1103, 1121, 1139, 1157, 1175, 1193, 1211, 1229, 1246, 1264, 1282, 1299, 1317, 1335, + 1352, 1370, 1387, 1405, 1422, 1440, 1457, 1474, 1491, 1509, 1526, 1543, 1560, 1577, 1595, 1627, 1660, + 1693, 1725, 1758, 1791, 1824, 1856, 1889, 1922, 1954, 1987, 2020, 2052, 2085, 2118, 2150, 2183, 2216, + 2248, 2281, 2313, 2346, 2378, 2411, 2459, 2508, 2556, 2605, 2653, 2701, 2750, 2798, 2847, 2895, 2943, + 2992, 3040, 3088, 3137, 3185, 3234, 3298, 3362, 3426, 3491, 3555, 3619, 3684, 3748, 3812, 3876, 3941, + 4005, 4069, 4149, 4230, 4310, 4390, 4470, 4550, 4631, 4711, 4791, 4871, 4967, 5064, 5160, 5256, 5352, + 5448, 5544, 5641, 5737, 5849, 5961, 6073, 6185, 6297, 6410, 6522, 6650, 6778, 6906, 7034, 7162, 7290, + 7435, 7579, 7723, 7867, 8011, 8155, 8315, 8475, 8635, 8795, 8956, 9132, 9308, 9484, 9660, 9836, 10028, + 10220, 10412, 10604, 10812, 11020, 11228, 11437, 11661, 11885, 12109, 12333, 12573, 12813, 13053, 13309, + 13565, 13821, 14093, 14365, 14637, 14925, 15213, 15502, 15806, 16110, 16414, 16734, 17054, 17390, 17726, + 18062, 18414, 18766, 19134, 19502, 19886, 20270, 20670, 21070, 21486, 21902, 22334, 22766, 23214, 23662, + 24126, 24590, 25070, 25551, 26047, 26559, 27071, 27599, 28143, 28687, 29247 + ]; public static short DcQuant(int qindex, int delta, BitDepth bitDepth) { switch (bitDepth) { - case BitDepth.Bits8: - return _dcQlookup[Math.Clamp(qindex + delta, 0, MaxQ)]; - case BitDepth.Bits10: - return _dcQlookup10[Math.Clamp(qindex + delta, 0, MaxQ)]; - case BitDepth.Bits12: - return _dcQlookup12[Math.Clamp(qindex + delta, 0, MaxQ)]; + case BitDepth.Bits8: return _dcQlookup[Math.Clamp(qindex + delta, 0, MaxQ)]; + case BitDepth.Bits10: return _dcQlookup10[Math.Clamp(qindex + delta, 0, MaxQ)]; + case BitDepth.Bits12: return _dcQlookup12[Math.Clamp(qindex + delta, 0, MaxQ)]; default: - Debug.Assert(false, "bit_depth should be VPX_BITS_8, VPX_BITS_10 or VPX_BITS_12"); - + Debug.Assert(false, "bitDepth should be Bits8, Bits10 or Bits12"); return -1; } } @@ -175,30 +130,13 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { switch (bitDepth) { - case BitDepth.Bits8: - return _acQlookup[Math.Clamp(qindex + delta, 0, MaxQ)]; - case BitDepth.Bits10: - return _acQlookup10[Math.Clamp(qindex + delta, 0, MaxQ)]; - case BitDepth.Bits12: - return _acQlookup12[Math.Clamp(qindex + delta, 0, MaxQ)]; + case BitDepth.Bits8: return _acQlookup[Math.Clamp(qindex + delta, 0, MaxQ)]; + case BitDepth.Bits10: return _acQlookup10[Math.Clamp(qindex + delta, 0, MaxQ)]; + case BitDepth.Bits12: return _acQlookup12[Math.Clamp(qindex + delta, 0, MaxQ)]; default: - Debug.Assert(false, "bit_depth should be VPX_BITS_8, VPX_BITS_10 or VPX_BITS_12"); - + Debug.Assert(false, "bitDepth should be Bits8, Bits10 or Bits12"); return -1; } } - - public static int GetQIndex(ref Segmentation seg, int segmentId, int baseQIndex) - { - if (seg.IsSegFeatureActive(segmentId, SegLvlFeatures.SegLvlAltQ) != 0) - { - int data = seg.GetSegData(segmentId, SegLvlFeatures.SegLvlAltQ); - int segQIndex = seg.AbsDelta == Constants.SegmentAbsData ? data : baseQIndex + data; - - return Math.Clamp(segQIndex, 0, MaxQ); - } - - return baseQIndex; - } } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/ReadBitBuffer.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/ReadBitBuffer.cs new file mode 100644 index 000000000..a4e07a1c0 --- /dev/null +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/ReadBitBuffer.cs @@ -0,0 +1,84 @@ +using Ryujinx.Graphics.Nvdec.Vp9.Common; +using Ryujinx.Graphics.Nvdec.Vp9.Types; +using System; + +namespace Ryujinx.Graphics.Nvdec.Vp9 +{ + public ref struct ReadBitBuffer + { + public ReadOnlySpan BitBuffer; + public ulong BitOffset; + public object ErrorHandlerData; + + public int DecodeUnsignedMax(int max) + { + int data = ReadLiteral(BitUtils.GetUnsignedBits((uint)max)); + return data > max ? max : data; + } + + public ulong BytesRead() + { + return (BitOffset + 7) >> 3; + } + + public int ReadBit() + { + ulong off = BitOffset; + ulong p = off >> 3; + int q = 7 - (int)(off & 0x7); + if (p < (ulong)BitBuffer.Length) + { + int bit = (BitBuffer[(int)p] >> q) & 1; + BitOffset = off + 1; + return bit; + } + + return 0; + } + + public int ReadLiteral(int bits) + { + int value = 0, bit; + for (bit = bits - 1; bit >= 0; bit--) + { + value |= ReadBit() << bit; + } + + return value; + } + + public int ReadSignedLiteral(int bits) + { + int value = ReadLiteral(bits); + return ReadBit() != 0 ? -value : value; + } + + public int ReadInvSignedLiteral(int bits) + { + return ReadSignedLiteral(bits); + } + + public int ReadDeltaQ() + { + return ReadBit() != 0 ? ReadSignedLiteral(4) : 0; + } + + public void ReadFrameSize(out int width, out int height) + { + width = ReadLiteral(16) + 1; + height = ReadLiteral(16) + 1; + } + + public BitstreamProfile ReadProfile() + { + int profile = ReadBit(); + profile |= ReadBit() << 1; + if (profile > 2) + { + profile += ReadBit(); + } + + return (BitstreamProfile)profile; + } + } +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/ReconInter.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/ReconInter.cs index fdb68291a..35dbf1cab 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/ReconInter.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/ReconInter.cs @@ -1,4 +1,4 @@ -using Ryujinx.Common.Memory; +using Ryujinx.Common.Memory; using Ryujinx.Graphics.Nvdec.Vp9.Types; using System; using System.Diagnostics; @@ -77,65 +77,38 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 bd); } - private static int RoundMvCompQ4(int value) + public static int RoundMvCompQ4(int value) { return (value < 0 ? value - 2 : value + 2) / 4; } - private static Mv MiMvPredQ4(ref ModeInfo mi, int idx) - { - return new Mv - { - Row = (short)RoundMvCompQ4( - mi.Bmi[0].Mv[idx].Row + mi.Bmi[1].Mv[idx].Row + - mi.Bmi[2].Mv[idx].Row + mi.Bmi[3].Mv[idx].Row), - Col = (short)RoundMvCompQ4( - mi.Bmi[0].Mv[idx].Col + mi.Bmi[1].Mv[idx].Col + - mi.Bmi[2].Mv[idx].Col + mi.Bmi[3].Mv[idx].Col), - }; - } - - private static int RoundMvCompQ2(int value) + public static int RoundMvCompQ2(int value) { return (value < 0 ? value - 1 : value + 1) / 2; } - private static Mv MiMvPredQ2(ref ModeInfo mi, int idx, int block0, int block1) - { - return new Mv - { - Row = (short)RoundMvCompQ2( - mi.Bmi[block0].Mv[idx].Row + - mi.Bmi[block1].Mv[idx].Row), - Col = (short)RoundMvCompQ2( - mi.Bmi[block0].Mv[idx].Col + - mi.Bmi[block1].Mv[idx].Col), - }; - } - public static Mv ClampMvToUmvBorderSb(ref MacroBlockD xd, ref Mv srcMv, int bw, int bh, int ssX, int ssY) { // If the MV points so far into the UMV border that no visible pixels // are used for reconstruction, the subpel part of the MV can be // discarded and the MV limited to 16 pixels with equivalent results. - int spelLeft = (Constants.Vp9InterpExtend + bw) << SubpelBits; + int spelLeft = (Constants.InterpExtend + bw) << SubpelBits; int spelRight = spelLeft - SubpelShifts; - int spelTop = (Constants.Vp9InterpExtend + bh) << SubpelBits; + int spelTop = (Constants.InterpExtend + bh) << SubpelBits; int spelBottom = spelTop - SubpelShifts; Mv clampedMv = new() { - Row = (short)(srcMv.Row * (1 << (1 - ssY))), - Col = (short)(srcMv.Col * (1 << (1 - ssX))), + Row = (short)(srcMv.Row * (1 << (1 - ssY))), Col = (short)(srcMv.Col * (1 << (1 - ssX))) }; Debug.Assert(ssX <= 1); Debug.Assert(ssY <= 1); - clampedMv.ClampMv( - xd.MbToLeftEdge * (1 << (1 - ssX)) - spelLeft, - xd.MbToRightEdge * (1 << (1 - ssX)) + spelRight, - xd.MbToTopEdge * (1 << (1 - ssY)) - spelTop, - xd.MbToBottomEdge * (1 << (1 - ssY)) + spelBottom); + clampedMv.Clamp( + (xd.MbToLeftEdge * (1 << (1 - ssX))) - spelLeft, + (xd.MbToRightEdge * (1 << (1 - ssX))) + spelRight, + (xd.MbToTopEdge * (1 << (1 - ssY))) - spelTop, + (xd.MbToBottomEdge * (1 << (1 - ssY))) + spelBottom); return clampedMv; } @@ -150,18 +123,19 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 res = mi.Bmi[block].Mv[refr]; break; case 1: - res = MiMvPredQ2(ref mi, refr, block, block + 2); + res = mi.MvPredQ2(refr, block, block + 2); break; case 2: - res = MiMvPredQ2(ref mi, refr, block, block + 1); + res = mi.MvPredQ2(refr, block, block + 1); break; case 3: - res = MiMvPredQ4(ref mi, refr); + res = mi.MvPredQ4(refr); break; default: Debug.Assert(ssIdx <= 3 && ssIdx >= 0); break; } + return res; } @@ -169,8 +143,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { int x = !sf.IsNull ? sf.Value.ScaleValueX(xOffset) : xOffset; int y = !sf.IsNull ? sf.Value.ScaleValueY(yOffset) : yOffset; - - return y * stride + x; + return (y * stride) + x; } private static void SetupPredPlanes( @@ -203,12 +176,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 strides[0] = src.Stride; strides[1] = src.UvStride; strides[2] = src.UvStride; - int i; - for (i = 0; i < Constants.MaxMbPlane; ++i) + for (int i = 0; i < Constants.MaxMbPlane; ++i) { ref MacroBlockDPlane pd = ref planes[i]; - SetupPredPlanes(ref pd.Dst, buffers[i], strides[i], miRow, miCol, Ptr.Null, pd.SubsamplingX, pd.SubsamplingY); + SetupPredPlanes(ref pd.Dst, buffers[i], strides[i], miRow, miCol, Ptr.Null, + pd.SubsamplingX, pd.SubsamplingY); } } @@ -230,14 +203,14 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 strides[0] = src.Stride; strides[1] = src.UvStride; strides[2] = src.UvStride; - int i; - for (i = 0; i < Constants.MaxMbPlane; ++i) + for (int i = 0; i < Constants.MaxMbPlane; ++i) { ref MacroBlockDPlane pd = ref xd.Plane[i]; - SetupPredPlanes(ref pd.Pre[idx], buffers[i], strides[i], miRow, miCol, sf, pd.SubsamplingX, pd.SubsamplingY); + SetupPredPlanes(ref pd.Pre[idx], buffers[i], strides[i], miRow, miCol, sf, pd.SubsamplingX, + pd.SubsamplingY); } } } } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/ReconIntra.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/ReconIntra.cs index 768c9acf4..877469787 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/ReconIntra.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/ReconIntra.cs @@ -1,4 +1,4 @@ -using Ryujinx.Graphics.Nvdec.Vp9.Common; +using Ryujinx.Graphics.Nvdec.Vp9.Common; using Ryujinx.Graphics.Nvdec.Vp9.Types; using System; using static Ryujinx.Graphics.Nvdec.Vp9.Dsp.IntraPred; @@ -7,7 +7,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { internal static class ReconIntra { - public static readonly TxType[] IntraModeToTxTypeLookup = { + public static readonly TxType[] IntraModeToTxTypeLookup = + [ TxType.DctDct, // DC TxType.AdstDct, // V TxType.DctAdst, // H @@ -17,15 +18,15 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 TxType.DctAdst, // D153 TxType.DctAdst, // D207 TxType.AdstDct, // D63 - TxType.AdstAdst, // TM - }; + TxType.AdstAdst // TM + ]; private const int NeedLeft = 1 << 1; private const int NeedAbove = 1 << 2; private const int NeedAboveRight = 1 << 3; - private static ReadOnlySpan ExtendModes => new byte[] - { + private static ReadOnlySpan ExtendModes => + [ NeedAbove | NeedLeft, // DC NeedAbove, // V NeedLeft, // H @@ -35,232 +36,104 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 NeedLeft | NeedAbove, // D153 NeedLeft, // D207 NeedAboveRight, // D63 - NeedLeft | NeedAbove, // TM - }; + NeedLeft | NeedAbove // TM + ]; private unsafe delegate void IntraPredFn(byte* dst, int stride, byte* above, byte* left); - private static readonly unsafe IntraPredFn[][] _pred = { - new IntraPredFn[] - { - null, - null, - null, - null, - }, - new IntraPredFn[] - { - VPredictor4x4, - VPredictor8x8, - VPredictor16x16, - VPredictor32x32, - }, - new IntraPredFn[] - { - HPredictor4x4, - HPredictor8x8, - HPredictor16x16, - HPredictor32x32, - }, - new IntraPredFn[] - { - D45Predictor4x4, - D45Predictor8x8, - D45Predictor16x16, - D45Predictor32x32, - }, - new IntraPredFn[] - { - D135Predictor4x4, - D135Predictor8x8, - D135Predictor16x16, - D135Predictor32x32, - }, - new IntraPredFn[] - { - D117Predictor4x4, - D117Predictor8x8, - D117Predictor16x16, - D117Predictor32x32, - }, - new IntraPredFn[] - { - D153Predictor4x4, - D153Predictor8x8, - D153Predictor16x16, - D153Predictor32x32, - }, - new IntraPredFn[] - { - D207Predictor4x4, - D207Predictor8x8, - D207Predictor16x16, - D207Predictor32x32, - }, - new IntraPredFn[] - { - D63Predictor4x4, - D63Predictor8x8, - D63Predictor16x16, - D63Predictor32x32, - }, - new IntraPredFn[] - { - TMPredictor4x4, - TMPredictor8x8, - TMPredictor16x16, - TMPredictor32x32, - }, - }; + private static readonly unsafe IntraPredFn[][] _pred = + [ + [null, null, null, null], + [VPredictor4X4, VPredictor8X8, VPredictor16X16, VPredictor32X32], + [HPredictor4X4, HPredictor8X8, HPredictor16X16, HPredictor32X32], + [D45Predictor4X4, D45Predictor8X8, D45Predictor16X16, D45Predictor32X32], + [D135Predictor4X4, D135Predictor8X8, D135Predictor16X16, D135Predictor32X32], + [D117Predictor4X4, D117Predictor8X8, D117Predictor16X16, D117Predictor32X32], + [D153Predictor4X4, D153Predictor8X8, D153Predictor16X16, D153Predictor32X32], + [D207Predictor4X4, D207Predictor8X8, D207Predictor16X16, D207Predictor32X32], + [D63Predictor4X4, D63Predictor8X8, D63Predictor16X16, D63Predictor32X32], + [TmPredictor4X4, TmPredictor8X8, TmPredictor16X16, TmPredictor32X32] + ]; - private static readonly unsafe IntraPredFn[][][] _dcPred = { - new[] - { - new IntraPredFn[] - { - Dc128Predictor4x4, - Dc128Predictor8x8, - Dc128Predictor16x16, - Dc128Predictor32x32, - }, - new IntraPredFn[] - { - DcTopPredictor4x4, - DcTopPredictor8x8, - DcTopPredictor16x16, - DcTopPredictor32x32, - }, - }, - new[] - { - new IntraPredFn[] - { - DcLeftPredictor4x4, - DcLeftPredictor8x8, - DcLeftPredictor16x16, - DcLeftPredictor32x32, - }, - new IntraPredFn[] - { - DcPredictor4x4, - DcPredictor8x8, - DcPredictor16x16, - DcPredictor32x32, - }, - }, - }; + private static readonly unsafe IntraPredFn[][][] _dcPred = + [ + [ + [ + Dc128Predictor4X4, Dc128Predictor8X8, Dc128Predictor16X16, Dc128Predictor32X32 + ], + [ + DcTopPredictor4X4, DcTopPredictor8X8, DcTopPredictor16X16, DcTopPredictor32X32 + ] + ], + [ + [ + DcLeftPredictor4X4, DcLeftPredictor8X8, DcLeftPredictor16X16, DcLeftPredictor32X32 + ], + [DcPredictor4X4, DcPredictor8X8, DcPredictor16X16, DcPredictor32X32] + ] + ]; private unsafe delegate void IntraHighPredFn(ushort* dst, int stride, ushort* above, ushort* left, int bd); - private static readonly unsafe IntraHighPredFn[][] _predHigh = { - new IntraHighPredFn[] - { - null, - null, - null, - null, - }, - new IntraHighPredFn[] - { - HighbdVPredictor4x4, - HighbdVPredictor8x8, - HighbdVPredictor16x16, - HighbdVPredictor32x32, - }, - new IntraHighPredFn[] - { - HighbdHPredictor4x4, - HighbdHPredictor8x8, - HighbdHPredictor16x16, - HighbdHPredictor32x32, - }, - new IntraHighPredFn[] - { - HighbdD45Predictor4x4, - HighbdD45Predictor8x8, - HighbdD45Predictor16x16, - HighbdD45Predictor32x32, - }, - new IntraHighPredFn[] - { - HighbdD135Predictor4x4, - HighbdD135Predictor8x8, - HighbdD135Predictor16x16, - HighbdD135Predictor32x32, - }, - new IntraHighPredFn[] - { - HighbdD117Predictor4x4, - HighbdD117Predictor8x8, - HighbdD117Predictor16x16, - HighbdD117Predictor32x32, - }, - new IntraHighPredFn[] - { - HighbdD153Predictor4x4, - HighbdD153Predictor8x8, - HighbdD153Predictor16x16, - HighbdD153Predictor32x32, - }, - new IntraHighPredFn[] - { - HighbdD207Predictor4x4, - HighbdD207Predictor8x8, - HighbdD207Predictor16x16, - HighbdD207Predictor32x32, - }, - new IntraHighPredFn[] - { - HighbdD63Predictor4x4, - HighbdD63Predictor8x8, - HighbdD63Predictor16x16, - HighbdD63Predictor32x32, - }, - new IntraHighPredFn[] - { - HighbdTMPredictor4x4, - HighbdTMPredictor8x8, - HighbdTMPredictor16x16, - HighbdTMPredictor32x32, - }, - }; + private static readonly unsafe IntraHighPredFn[][] _predHigh = + [ + [null, null, null, null], + [ + HighbdVPredictor4X4, HighbdVPredictor8X8, HighbdVPredictor16X16, HighbdVPredictor32X32 + ], + [ + HighbdHPredictor4X4, HighbdHPredictor8X8, HighbdHPredictor16X16, HighbdHPredictor32X32 + ], + [ + HighbdD45Predictor4X4, HighbdD45Predictor8X8, HighbdD45Predictor16X16, HighbdD45Predictor32X32 + ], + [ + HighbdD135Predictor4X4, HighbdD135Predictor8X8, HighbdD135Predictor16X16, + HighbdD135Predictor32X32 + ], + [ + HighbdD117Predictor4X4, HighbdD117Predictor8X8, HighbdD117Predictor16X16, + HighbdD117Predictor32X32 + ], + [ + HighbdD153Predictor4X4, HighbdD153Predictor8X8, HighbdD153Predictor16X16, + HighbdD153Predictor32X32 + ], + [ + HighbdD207Predictor4X4, HighbdD207Predictor8X8, HighbdD207Predictor16X16, + HighbdD207Predictor32X32 + ], + [ + HighbdD63Predictor4X4, HighbdD63Predictor8X8, HighbdD63Predictor16X16, HighbdD63Predictor32X32 + ], + [ + HighbdTmPredictor4X4, HighbdTmPredictor8X8, HighbdTmPredictor16X16, HighbdTmPredictor32X32 + ] + ]; - private static readonly unsafe IntraHighPredFn[][][] _dcPredHigh = { - new[] - { - new IntraHighPredFn[] - { - HighbdDc128Predictor4x4, - HighbdDc128Predictor8x8, - HighbdDc128Predictor16x16, - HighbdDc128Predictor32x32, - }, - new IntraHighPredFn[] - { - HighbdDcTopPredictor4x4, - HighbdDcTopPredictor8x8, - HighbdDcTopPredictor16x16, - HighbdDcTopPredictor32x32, - }, - }, - new[] - { - new IntraHighPredFn[] - { - HighbdDcLeftPredictor4x4, - HighbdDcLeftPredictor8x8, - HighbdDcLeftPredictor16x16, - HighbdDcLeftPredictor32x32, - }, - new IntraHighPredFn[] - { - HighbdDcPredictor4x4, - HighbdDcPredictor8x8, - HighbdDcPredictor16x16, - HighbdDcPredictor32x32, - }, - }, - }; + private static readonly unsafe IntraHighPredFn[][][] _dcPredHigh = + [ + [ + [ + HighbdDc128Predictor4X4, HighbdDc128Predictor8X8, HighbdDc128Predictor16X16, + HighbdDc128Predictor32X32 + ], + [ + HighbdDcTopPredictor4X4, HighbdDcTopPredictor8X8, HighbdDcTopPredictor16X16, + HighbdDcTopPredictor32X32 + ] + ], + [ + [ + HighbdDcLeftPredictor4X4, HighbdDcLeftPredictor8X8, HighbdDcLeftPredictor16X16, + HighbdDcLeftPredictor32X32 + ], + [ + HighbdDcPredictor4X4, HighbdDcPredictor8X8, HighbdDcPredictor16X16, + HighbdDcPredictor32X32 + ] + ] + ]; private static unsafe void BuildIntraPredictorsHigh( ref MacroBlockD xd, @@ -327,7 +200,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { for (i = 0; i < bs; ++i) { - leftCol[i] = refr[i * refStride - 1]; + leftCol[i] = refr[(i * refStride) - 1]; } } else @@ -335,12 +208,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 int extendBottom = frameHeight - y0; for (i = 0; i < extendBottom; ++i) { - leftCol[i] = refr[i * refStride - 1]; + leftCol[i] = refr[(i * refStride) - 1]; } for (; i < bs; ++i) { - leftCol[i] = refr[(extendBottom - 1) * refStride - 1]; + leftCol[i] = refr[((extendBottom - 1) * refStride) - 1]; } } } @@ -349,7 +222,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 /* faster path if the block does not need extension */ for (i = 0; i < bs; ++i) { - leftCol[i] = refr[i * refStride - 1]; + leftCol[i] = refr[(i * refStride) - 1]; } } } @@ -391,6 +264,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 MemoryUtil.Copy(aboveRow, aboveRef, bs); } } + aboveRow[-1] = leftAvailable != 0 ? aboveRef[-1] : (ushort)(baseVal + 1); } else @@ -409,7 +283,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 if (xd.MbToRightEdge < 0) { /* slower path if the block needs border extension */ - if (x0 + 2 * bs <= frameWidth) + if (x0 + (2 * bs) <= frameWidth) { if (rightAvailable != 0 && bs == 4) { @@ -427,7 +301,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 if (rightAvailable != 0 && bs == 4) { MemoryUtil.Copy(aboveRow, aboveRef, r); - MemoryUtil.Fill(aboveRow + r, aboveRow[r - 1], x0 + 2 * bs - frameWidth); + MemoryUtil.Fill(aboveRow + r, aboveRow[r - 1], x0 + (2 * bs) - frameWidth); } else { @@ -439,8 +313,9 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { int r = frameWidth - x0; MemoryUtil.Copy(aboveRow, aboveRef, r); - MemoryUtil.Fill(aboveRow + r, aboveRow[r - 1], x0 + 2 * bs - frameWidth); + MemoryUtil.Fill(aboveRow + r, aboveRow[r - 1], x0 + (2 * bs) - frameWidth); } + aboveRow[-1] = leftAvailable != 0 ? aboveRef[-1] : (ushort)(baseVal + 1); } else @@ -544,7 +419,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { for (i = 0; i < bs; ++i) { - leftCol[i] = refr[i * refStride - 1]; + leftCol[i] = refr[(i * refStride) - 1]; } } else @@ -552,12 +427,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 int extendBottom = frameHeight - y0; for (i = 0; i < extendBottom; ++i) { - leftCol[i] = refr[i * refStride - 1]; + leftCol[i] = refr[(i * refStride) - 1]; } for (; i < bs; ++i) { - leftCol[i] = refr[(extendBottom - 1) * refStride - 1]; + leftCol[i] = refr[((extendBottom - 1) * refStride) - 1]; } } } @@ -566,7 +441,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 /* Faster path if the block does not need extension */ for (i = 0; i < bs; ++i) { - leftCol[i] = refr[i * refStride - 1]; + leftCol[i] = refr[(i * refStride) - 1]; } } } @@ -608,6 +483,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 MemoryUtil.Copy(aboveRow, aboveRef, bs); } } + aboveRow[-1] = leftAvailable != 0 ? aboveRef[-1] : (byte)129; } else @@ -626,7 +502,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 if (xd.MbToRightEdge < 0) { /* Slower path if the block needs border extension */ - if (x0 + 2 * bs <= frameWidth) + if (x0 + (2 * bs) <= frameWidth) { if (rightAvailable != 0 && bs == 4) { @@ -644,7 +520,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 if (rightAvailable != 0 && bs == 4) { MemoryUtil.Copy(aboveRow, aboveRef, r); - MemoryUtil.Fill(aboveRow + r, aboveRow[r - 1], x0 + 2 * bs - frameWidth); + MemoryUtil.Fill(aboveRow + r, aboveRow[r - 1], x0 + (2 * bs) - frameWidth); } else { @@ -656,7 +532,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { int r = frameWidth - x0; MemoryUtil.Copy(aboveRow, aboveRef, r); - MemoryUtil.Fill(aboveRow + r, aboveRow[r - 1], x0 + 2 * bs - frameWidth); + MemoryUtil.Fill(aboveRow + r, aboveRow[r - 1], x0 + (2 * bs) - frameWidth); } } else @@ -679,6 +555,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 } } } + aboveRow[-1] = leftAvailable != 0 ? aboveRef[-1] : (byte)129; } else @@ -716,7 +593,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 int txw = 1 << (int)txSize; int haveTop = loff != 0 || !xd.AboveMi.IsNull ? 1 : 0; int haveLeft = aoff != 0 || !xd.LeftMi.IsNull ? 1 : 0; - int haveRight = (aoff + txw) < bw ? 1 : 0; + int haveRight = aoff + txw < bw ? 1 : 0; int x = aoff * 4; int y = loff * 4; @@ -736,9 +613,9 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 x, y, plane); - return; } + BuildIntraPredictors( ref xd, refr, @@ -755,4 +632,4 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 plane); } } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/TileBuffer.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/TileBuffer.cs index b6adb95ff..18b9d7f47 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/TileBuffer.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/TileBuffer.cs @@ -8,4 +8,4 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 public ArrayPtr Data; public int Size; } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/TileWorkerData.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/TileWorkerData.cs index a9a7d566e..8c62dca75 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/TileWorkerData.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/TileWorkerData.cs @@ -12,9 +12,20 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 public int BufEnd; public Reader BitReader; public Vp9BackwardUpdates Counts; + public MacroBlockD Xd; + /* dqcoeff are shared by all the planes. So planes must be decoded serially */ public Array32> Dqcoeff; public InternalErrorInfo ErrorInfo; + + public int DecPartitionPlaneContext(int miRow, int miCol, int bsl) + { + ref sbyte aboveCtx = ref Xd.AboveSegContext[miCol]; + ref sbyte leftCtx = ref Xd.LeftSegContext[miRow & Constants.MiMask]; + int above = (aboveCtx >> bsl) & 1, left = (leftCtx >> bsl) & 1; + + return (left * 2) + above + (bsl * Constants.PartitionPloffset); + } } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/BModeInfo.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/BModeInfo.cs index 4ff82820e..a9d32d0b4 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/BModeInfo.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/BModeInfo.cs @@ -7,4 +7,4 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types public PredictionMode Mode; public Array2 Mv; // First, second inter predictor motion vectors } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/BitstreamProfile.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/BitstreamProfile.cs new file mode 100644 index 000000000..13293f460 --- /dev/null +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/BitstreamProfile.cs @@ -0,0 +1,11 @@ +namespace Ryujinx.Graphics.Nvdec.Vp9.Types +{ + public enum BitstreamProfile + { + Profile0, + Profile1, + Profile2, + Profile3, + MaxProfiles + } +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/BlockSize.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/BlockSize.cs index c7d4f7cc4..cee162ab7 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/BlockSize.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/BlockSize.cs @@ -1,21 +1,21 @@ -namespace Ryujinx.Graphics.Nvdec.Vp9.Types +namespace Ryujinx.Graphics.Nvdec.Vp9.Types { internal enum BlockSize { - Block4x4 = 0, - Block4x8 = 1, - Block8x4 = 2, - Block8x8 = 3, - Block8x16 = 4, - Block16x8 = 5, - Block16x16 = 6, - Block16x32 = 7, - Block32x16 = 8, - Block32x32 = 9, - Block32x64 = 10, - Block64x32 = 11, - Block64x64 = 12, - BlockSizes = 13, - BlockInvalid = BlockSizes, + Block4X4, + Block4X8, + Block8X4, + Block8X8, + Block8X16, + Block16X8, + Block16X16, + Block16X32, + Block32X16, + Block32X32, + Block32X64, + Block64X32, + Block64X64, + BlockSizes, + BlockInvalid = BlockSizes } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Buf2D.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Buf2D.cs index 1313a2e1f..575189490 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Buf2D.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Buf2D.cs @@ -7,4 +7,4 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types public ArrayPtr Buf; public int Stride; } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/BufferPool.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/BufferPool.cs new file mode 100644 index 000000000..bfe87d5a5 --- /dev/null +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/BufferPool.cs @@ -0,0 +1,18 @@ +using Ryujinx.Common.Memory; + +namespace Ryujinx.Graphics.Nvdec.Vp9.Types +{ + internal struct BufferPool + { + // Private data associated with the frame buffer callbacks. + public Ptr CbPriv; + + // vpx_get_frame_buffer_cb_fn_t get_fb_cb; + // vpx_release_frame_buffer_cb_fn_t release_fb_cb; + + public Array12 FrameBufs; + + // Frame buffers allocated internally by the codec. + public InternalFrameBufferList IntFrameBuffers; + } +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/FrameType.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/FrameType.cs index 21f021c5b..8b0935e29 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/FrameType.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/FrameType.cs @@ -5,4 +5,4 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types KeyFrame = 0, InterFrame = 1, } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/LoopFilter.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/LoopFilter.cs index d2b295476..4207c54d7 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/LoopFilter.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/LoopFilter.cs @@ -23,5 +23,18 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types public ArrayPtr Lfm; public int LfmStride; + + public void SetDefaultLfDeltas() + { + ModeRefDeltaEnabled = true; + ModeRefDeltaUpdate = true; + + RefDeltas[Constants.IntraFrame] = 1; + RefDeltas[Constants.LastFrame] = 0; + RefDeltas[Constants.GoldenFrame] = -1; + RefDeltas[Constants.AltRefFrame] = -1; + ModeDeltas[0] = 0; + ModeDeltas[1] = 0; + } } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/LoopFilterInfoN.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/LoopFilterInfoN.cs index be6243071..2a88df3e1 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/LoopFilterInfoN.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/LoopFilterInfoN.cs @@ -7,4 +7,4 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types public Array64 Lfthr; public Array8>> Lvl; } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/LoopFilterMask.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/LoopFilterMask.cs index 879cbe15b..4aea49425 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/LoopFilterMask.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/LoopFilterMask.cs @@ -15,10 +15,10 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types { public Array4 LeftY; public Array4 AboveY; - public ulong Int4x4Y; + public ulong Int4X4Y; public Array4 LeftUv; public Array4 AboveUv; - public ushort Int4x4Uv; + public ushort Int4X4Uv; public Array64 LflY; } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/LoopFilterThresh.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/LoopFilterThresh.cs index 546213215..7fa3c6355 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/LoopFilterThresh.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/LoopFilterThresh.cs @@ -12,4 +12,4 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types public Array16 HevThr; #pragma warning restore CS0649 } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/MacroBlockD.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/MacroBlockD.cs index 6305664df..bc6b7be32 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/MacroBlockD.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/MacroBlockD.cs @@ -1,4 +1,5 @@ -using Ryujinx.Common.Memory; +using Ryujinx.Common.Memory; +using Ryujinx.Graphics.Nvdec.Vp9.Common; using Ryujinx.Graphics.Video; namespace Ryujinx.Graphics.Nvdec.Vp9.Types @@ -54,7 +55,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types public Ptr ErrorInfo; - public readonly int GetPredContextSegId() + public int GetPredContextSegId() { sbyte aboveSip = !AboveMi.IsNull ? AboveMi.Value.SegIdPredicted : (sbyte)0; sbyte leftSip = !LeftMi.IsNull ? LeftMi.Value.SegIdPredicted : (sbyte)0; @@ -62,15 +63,14 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types return aboveSip + leftSip; } - public readonly int GetSkipContext() + public int GetSkipContext() { int aboveSkip = !AboveMi.IsNull ? AboveMi.Value.Skip : 0; int leftSkip = !LeftMi.IsNull ? LeftMi.Value.Skip : 0; - return aboveSkip + leftSkip; } - public readonly int GetPredContextSwitchableInterp() + public int GetPredContextSwitchableInterp() { // Note: // The mode info data structure has a one element border above and to the @@ -83,18 +83,18 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types { return leftType; } - else if (leftType == Constants.SwitchableFilters) + + if (leftType == Constants.SwitchableFilters) { return aboveType; } - else if (aboveType == Constants.SwitchableFilters) + + if (aboveType == Constants.SwitchableFilters) { return leftType; } - else - { - return Constants.SwitchableFilters; - } + + return Constants.SwitchableFilters; } // The mode info data structure has a one element border above and to the @@ -104,20 +104,22 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types // 1 - intra/inter, inter/intra // 2 - intra/--, --/intra // 3 - intra/intra - public readonly int GetIntraInterContext() + public int GetIntraInterContext() { if (!AboveMi.IsNull && !LeftMi.IsNull) - { // Both edges available + { + // Both edges available bool aboveIntra = !AboveMi.Value.IsInterBlock(); bool leftIntra = !LeftMi.Value.IsInterBlock(); - - return leftIntra && aboveIntra ? 3 : (leftIntra || aboveIntra ? 1 : 0); + return leftIntra && aboveIntra ? 3 : leftIntra || aboveIntra ? 1 : 0; } if (!AboveMi.IsNull || !LeftMi.IsNull) - { // One edge available + { + // One edge available return 2 * (!(!AboveMi.IsNull ? AboveMi.Value : LeftMi.Value).IsInterBlock() ? 1 : 0); } + return 0; } @@ -125,11 +127,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types // The mode info data structure has a one element border above and to the // left of the entries corresponding to real blocks. // The prediction flags in these dummy entries are initialized to 0. - public readonly int GetTxSizeContext() + public int GetTxSizeContext() { int maxTxSize = (int)Luts.MaxTxSizeLookup[(int)Mi[0].Value.SbType]; - int aboveCtx = (!AboveMi.IsNull && AboveMi.Value.Skip == 0) ? (int)AboveMi.Value.TxSize : maxTxSize; - int leftCtx = (!LeftMi.IsNull && LeftMi.Value.Skip == 0) ? (int)LeftMi.Value.TxSize : maxTxSize; + int aboveCtx = !AboveMi.IsNull && AboveMi.Value.Skip == 0 ? (int)AboveMi.Value.TxSize : maxTxSize; + int leftCtx = !LeftMi.IsNull && LeftMi.Value.Skip == 0 ? (int)LeftMi.Value.TxSize : maxTxSize; if (LeftMi.IsNull) { leftCtx = aboveCtx; @@ -140,14 +142,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types aboveCtx = leftCtx; } - return (aboveCtx + leftCtx) > maxTxSize ? 1 : 0; + return aboveCtx + leftCtx > maxTxSize ? 1 : 0; } public void SetupBlockPlanes(int ssX, int ssY) { - int i; - - for (i = 0; i < Constants.MaxMbPlane; i++) + for (int i = 0; i < Constants.MaxMbPlane; i++) { Plane[i].SubsamplingX = i != 0 ? ssX : 0; Plane[i].SubsamplingY = i != 0 ? ssY : 0; @@ -158,25 +158,36 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types { int aboveIdx = miCol * 2; int leftIdx = (miRow * 2) & 15; - int i; - for (i = 0; i < Constants.MaxMbPlane; ++i) + + for (int i = 0; i < Constants.MaxMbPlane; ++i) { ref MacroBlockDPlane pd = ref Plane[i]; pd.AboveContext = AboveContext[i].Slice(aboveIdx >> pd.SubsamplingX); - pd.LeftContext = new ArrayPtr(ref LeftContext[i][leftIdx >> pd.SubsamplingY], 16 - (leftIdx >> pd.SubsamplingY)); + pd.LeftContext = new ArrayPtr(ref LeftContext[i][leftIdx >> pd.SubsamplingY], + 16 - (leftIdx >> pd.SubsamplingY)); } } internal void SetMiRowCol(ref TileInfo tile, int miRow, int bh, int miCol, int bw, int miRows, int miCols) { - MbToTopEdge = -((miRow * Constants.MiSize) * 8); - MbToBottomEdge = ((miRows - bh - miRow) * Constants.MiSize) * 8; - MbToLeftEdge = -((miCol * Constants.MiSize) * 8); - MbToRightEdge = ((miCols - bw - miCol) * Constants.MiSize) * 8; + MbToTopEdge = -(miRow * Constants.MiSize * 8); + MbToBottomEdge = (miRows - bh - miRow) * Constants.MiSize * 8; + MbToLeftEdge = -(miCol * Constants.MiSize * 8); + MbToRightEdge = (miCols - bw - miCol) * Constants.MiSize * 8; // Are edges available for intra prediction? - AboveMi = (miRow != 0) ? Mi[-MiStride] : Ptr.Null; - LeftMi = (miCol > tile.MiColStart) ? Mi[-1] : Ptr.Null; + AboveMi = miRow != 0 ? Mi[-MiStride] : Ptr.Null; + LeftMi = miCol > tile.MiColStart ? Mi[-1] : Ptr.Null; + } + + public unsafe void DecResetSkipContext() + { + for (int i = 0; i < Constants.MaxMbPlane; i++) + { + ref MacroBlockDPlane pd = ref Plane[i]; + MemoryUtil.Fill(pd.AboveContext.ToPointer(), (sbyte)0, pd.N4W); + MemoryUtil.Fill(pd.LeftContext.ToPointer(), (sbyte)0, pd.N4H); + } } } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/MacroBlockDPlane.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/MacroBlockDPlane.cs index df21a8d49..d5b64f078 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/MacroBlockDPlane.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/MacroBlockDPlane.cs @@ -15,7 +15,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types // Number of 4x4s in current block public ushort N4W, N4H; + // Log2 of N4W, N4H public byte N4Wl, N4Hl; } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/ModeInfo.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/ModeInfo.cs index e97ed424c..39f545a76 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/ModeInfo.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/ModeInfo.cs @@ -1,4 +1,4 @@ -using Ryujinx.Common.Memory; +using Ryujinx.Common.Memory; using System.Diagnostics; namespace Ryujinx.Graphics.Nvdec.Vp9.Types @@ -29,14 +29,13 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types public PredictionMode GetYMode(int block) { - return SbType < BlockSize.Block8x8 ? Bmi[block].Mode : Mode; + return SbType < BlockSize.Block8X8 ? Bmi[block].Mode : Mode; } - public readonly TxSize GetUvTxSize(ref MacroBlockDPlane pd) + public TxSize GetUvTxSize(ref MacroBlockDPlane pd) { - Debug.Assert(SbType < BlockSize.Block8x8 || - Luts.SsSizeLookup[(int)SbType][pd.SubsamplingX][pd.SubsamplingY] != BlockSize.BlockInvalid); - + Debug.Assert(SbType < BlockSize.Block8X8 || + Luts.SsSizeLookup[(int)SbType][pd.SubsamplingX][pd.SubsamplingY] != BlockSize.BlockInvalid); return Luts.UvTxsizeLookup[(int)SbType][(int)TxSize][pd.SubsamplingX][pd.SubsamplingY]; } @@ -50,17 +49,59 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types return RefFrame[1] > Constants.IntraFrame; } - private static readonly int[][] _idxNColumnToSubblock = { - new[] { 1, 2 }, new[] { 1, 3 }, new[] { 3, 2 }, new[] { 3, 3 }, - }; + private static readonly int[][] _idxNColumnToSubblock = + [ + [1, 2], [1, 3], [3, 2], [3, 3] + ]; // This function returns either the appropriate sub block or block's mv // on whether the block_size < 8x8 and we have check_sub_blocks set. public Mv GetSubBlockMv(int whichMv, int searchCol, int blockIdx) { - return blockIdx >= 0 && SbType < BlockSize.Block8x8 + return blockIdx >= 0 && SbType < BlockSize.Block8X8 ? Bmi[_idxNColumnToSubblock[blockIdx][searchCol == 0 ? 1 : 0]].Mv[whichMv] : Mv[whichMv]; } + + public Mv MvPredQ4(int idx) + { + Mv res = new() + { + Row = (short)ReconInter.RoundMvCompQ4( + Bmi[0].Mv[idx].Row + Bmi[1].Mv[idx].Row + + Bmi[2].Mv[idx].Row + Bmi[3].Mv[idx].Row), + Col = (short)ReconInter.RoundMvCompQ4( + Bmi[0].Mv[idx].Col + Bmi[1].Mv[idx].Col + + Bmi[2].Mv[idx].Col + Bmi[3].Mv[idx].Col) + }; + return res; + } + + public Mv MvPredQ2(int idx, int block0, int block1) + { + Mv res = new() + { + Row = (short)ReconInter.RoundMvCompQ2( + Bmi[block0].Mv[idx].Row + + Bmi[block1].Mv[idx].Row), + Col = (short)ReconInter.RoundMvCompQ2( + Bmi[block0].Mv[idx].Col + + Bmi[block1].Mv[idx].Col) + }; + return res; + } + + // Performs mv sign inversion if indicated by the reference frame combination. + public Mv ScaleMv(int refr, sbyte thisRefFrame, ref Array4 refSignBias) + { + Mv mv = Mv[refr]; + if (refSignBias[RefFrame[refr]] != refSignBias[thisRefFrame]) + { + mv.Row *= -1; + mv.Col *= -1; + } + + return mv; + } } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/MotionVectorContext.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/MotionVectorContext.cs index bad5af280..138b8a0ee 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/MotionVectorContext.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/MotionVectorContext.cs @@ -11,4 +11,4 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types BothIntra = 6, InvalidCase = 9, } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Mv.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Mv.cs index 9ccb51503..6e0accfb3 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Mv.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Mv.cs @@ -1,4 +1,4 @@ -using Ryujinx.Common.Memory; +using Ryujinx.Common.Memory; using Ryujinx.Graphics.Video; using System; using System.Diagnostics; @@ -10,98 +10,88 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types public short Row; public short Col; - private static ReadOnlySpan LogInBase2 => new byte[] - { - 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, - 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, - 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 10, - }; + private static ReadOnlySpan LogInBase2 => + [ + 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 10 + ]; - public readonly bool UseMvHp() + public bool UseHp() { const int KMvRefThresh = 64; // Threshold for use of high-precision 1/8 mv return Math.Abs(Row) < KMvRefThresh && Math.Abs(Col) < KMvRefThresh; } - public static bool MvJointVertical(MvJointType type) + public static bool JointVertical(MvJointType type) { - return type == MvJointType.MvJointHzvnz || type == MvJointType.MvJointHnzvnz; + return type == MvJointType.Hzvnz || type == MvJointType.Hnzvnz; } - public static bool MvJointHorizontal(MvJointType type) + public static bool JointHorizontal(MvJointType type) { - return type == MvJointType.MvJointHnzvz || type == MvJointType.MvJointHnzvnz; + return type == MvJointType.Hnzvz || type == MvJointType.Hnzvnz; } - private static int MvClassBase(MvClassType c) + private static int ClassBase(MvClassType c) { return c != 0 ? Constants.Class0Size << ((int)c + 2) : 0; } - private static MvClassType GetMvClass(int z, Ptr offset) + private static MvClassType GetClass(int z, Ptr offset) { - MvClassType c = (z >= Constants.Class0Size * 4096) ? MvClassType.MvClass10 : (MvClassType)LogInBase2[z >> 3]; + MvClassType c = z >= Constants.Class0Size * 4096 ? MvClassType.Class10 : (MvClassType)LogInBase2[z >> 3]; if (!offset.IsNull) { - offset.Value = z - MvClassBase(c); + offset.Value = z - ClassBase(c); } return c; } - private static void IncMvComponent(int v, ref Vp9BackwardUpdates counts, int comp, int incr, int usehp) + private static void IncComponent(int v, ref Vp9BackwardUpdates counts, int comp, int incr, int usehp) { - int s, z, c, o = 0, d, e, f; + int o = 0; Debug.Assert(v != 0); /* Should not be zero */ - s = v < 0 ? 1 : 0; + int s = v < 0 ? 1 : 0; counts.Sign[comp][s] += (uint)incr; - z = (s != 0 ? -v : v) - 1; /* Magnitude - 1 */ + int z = (s != 0 ? -v : v) - 1 /* Magnitude - 1 */; - c = (int)GetMvClass(z, new Ptr(ref o)); + int c = (int)GetClass(z, new Ptr(ref o)); counts.Classes[comp][c] += (uint)incr; - d = (o >> 3); /* Int mv data */ - f = (o >> 1) & 3; /* Fractional pel mv data */ - e = (o & 1); /* High precision mv data */ + int d = o >> 3 /* Int mv data */; + int f = (o >> 1) & 3 /* Fractional pel mv data */; + int e = o & 1 /* High precision mv data */; - if (c == (int)MvClassType.MvClass0) + if (c == (int)MvClassType.Class0) { counts.Class0[comp][d] += (uint)incr; counts.Class0Fp[comp][d][f] += (uint)incr; @@ -109,11 +99,10 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types } else { - int i; int b = c + Constants.Class0Bits - 1; // Number of bits - for (i = 0; i < b; ++i) + for (int i = 0; i < b; ++i) { - counts.Bits[comp][i][((d >> i) & 1)] += (uint)incr; + counts.Bits[comp][i][(d >> i) & 1] += (uint)incr; } counts.Fp[comp][f] += (uint)incr; @@ -121,56 +110,56 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types } } - private readonly MvJointType GetMvJoint() + public MvJointType GetJoint() { if (Row == 0) { - return Col == 0 ? MvJointType.MvJointZero : MvJointType.MvJointHnzvz; + return Col == 0 ? MvJointType.Zero : MvJointType.Hnzvz; } - return Col == 0 ? MvJointType.MvJointHzvnz : MvJointType.MvJointHnzvnz; + return Col == 0 ? MvJointType.Hzvnz : MvJointType.Hnzvnz; } - internal readonly void IncMv(Ptr counts) + internal void Inc(Ptr counts) { if (!counts.IsNull) { - MvJointType j = GetMvJoint(); + MvJointType j = GetJoint(); ++counts.Value.Joints[(int)j]; - if (MvJointVertical(j)) + if (JointVertical(j)) { - IncMvComponent(Row, ref counts.Value, 0, 1, 1); + IncComponent(Row, ref counts.Value, 0, 1, 1); } - if (MvJointHorizontal(j)) + if (JointHorizontal(j)) { - IncMvComponent(Col, ref counts.Value, 1, 1, 1); + IncComponent(Col, ref counts.Value, 1, 1, 1); } } } - public void ClampMv(int minCol, int maxCol, int minRow, int maxRow) + public void Clamp(int minCol, int maxCol, int minRow, int maxRow) { Col = (short)Math.Clamp(Col, minCol, maxCol); Row = (short)Math.Clamp(Row, minRow, maxRow); } - private const int MvBorder = (16 << 3); // Allow 16 pels in 1/8th pel units + private const int Border = 16 << 3; // Allow 16 pels in 1/8th pel units - public void ClampMvRef(ref MacroBlockD xd) + public void ClampRef(ref MacroBlockD xd) { - ClampMv( - xd.MbToLeftEdge - MvBorder, - xd.MbToRightEdge + MvBorder, - xd.MbToTopEdge - MvBorder, - xd.MbToBottomEdge + MvBorder); + Clamp( + xd.MbToLeftEdge - Border, + xd.MbToRightEdge + Border, + xd.MbToTopEdge - Border, + xd.MbToBottomEdge + Border); } - public void LowerMvPrecision(bool allowHP) + public void LowerPrecision(bool allowHp) { - bool useHP = allowHP && UseMvHp(); - if (!useHP) + bool useHp = allowHp && UseHp(); + if (!useHp) { if ((Row & 1) != 0) { @@ -183,5 +172,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types } } } + + public bool IsValid() + { + return Row is > Constants.MvLow and < Constants.MvUpp && + Col is > Constants.MvLow and < Constants.MvUpp; + } } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Mv32.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Mv32.cs index 5231ca6a6..d3561fe58 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Mv32.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Mv32.cs @@ -5,4 +5,4 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types public int Row; public int Col; } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/MvClassType.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/MvClassType.cs index 188eee0b0..0e233b2f2 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/MvClassType.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/MvClassType.cs @@ -2,16 +2,16 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types { internal enum MvClassType { - MvClass0 = 0, /* (0, 2] integer pel */ - MvClass1 = 1, /* (2, 4] integer pel */ - MvClass2 = 2, /* (4, 8] integer pel */ - MvClass3 = 3, /* (8, 16] integer pel */ - MvClass4 = 4, /* (16, 32] integer pel */ - MvClass5 = 5, /* (32, 64] integer pel */ - MvClass6 = 6, /* (64, 128] integer pel */ - MvClass7 = 7, /* (128, 256] integer pel */ - MvClass8 = 8, /* (256, 512] integer pel */ - MvClass9 = 9, /* (512, 1024] integer pel */ - MvClass10 = 10, /* (1024,2048] integer pel */ + Class0, /* (0, 2] integer pel */ + Class1, /* (2, 4] integer pel */ + Class2, /* (4, 8] integer pel */ + Class3, /* (8, 16] integer pel */ + Class4, /* (16, 32] integer pel */ + Class5, /* (32, 64] integer pel */ + Class6, /* (64, 128] integer pel */ + Class7, /* (128, 256] integer pel */ + Class8, /* (256, 512] integer pel */ + Class9, /* (512, 1024] integer pel */ + Class10 /* (1024,2048] integer pel */ } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/MvJointType.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/MvJointType.cs index 8cccf90cd..499c50c68 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/MvJointType.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/MvJointType.cs @@ -2,9 +2,9 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types { internal enum MvJointType { - MvJointZero = 0, /* Zero vector */ - MvJointHnzvz = 1, /* Vert zero, hor nonzero */ - MvJointHzvnz = 2, /* Hor zero, vert nonzero */ - MvJointHnzvnz = 3, /* Both components nonzero */ + Zero, /* Zero vector */ + Hnzvz, /* Vert zero, hor nonzero */ + Hzvnz, /* Hor zero, vert nonzero */ + Hnzvnz /* Both components nonzero */ } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/MvRef.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/MvRef.cs index 1d32a0927..06b082800 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/MvRef.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/MvRef.cs @@ -7,4 +7,4 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types public Array2 Mv; public Array2 RefFrame; } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/PartitionType.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/PartitionType.cs index c47557abf..8042071e4 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/PartitionType.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/PartitionType.cs @@ -9,4 +9,4 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types PartitionTypes, PartitionInvalid = PartitionTypes, } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/PlaneType.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/PlaneType.cs index 8b01cf3a1..ced1646e9 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/PlaneType.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/PlaneType.cs @@ -1,9 +1,9 @@ -namespace Ryujinx.Graphics.Nvdec.Vp9.Types +namespace Ryujinx.Graphics.Nvdec.Vp9.Types { internal enum PlaneType { - Y = 0, - Uv = 1, - PlaneTypes, + Y, + Uv, + PlaneTypes } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Position.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Position.cs index cee2fbe83..d055f225e 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Position.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Position.cs @@ -11,4 +11,4 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types Col = col; } } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/PredictionMode.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/PredictionMode.cs index 2d81b547e..a0f15905d 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/PredictionMode.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/PredictionMode.cs @@ -1,21 +1,21 @@ -namespace Ryujinx.Graphics.Nvdec.Vp9.Types +namespace Ryujinx.Graphics.Nvdec.Vp9.Types { internal enum PredictionMode { - DcPred = 0, // Average of above and left pixels - VPred = 1, // Vertical - HPred = 2, // Horizontal - D45Pred = 3, // Directional 45 deg = round(arctan(1 / 1) * 180 / pi) - D135Pred = 4, // Directional 135 deg = 180 - 45 - D117Pred = 5, // Directional 117 deg = 180 - 63 - D153Pred = 6, // Directional 153 deg = 180 - 27 - D207Pred = 7, // Directional 207 deg = 180 + 27 - D63Pred = 8, // Directional 63 deg = round(arctan(2 / 1) * 180 / pi) - TmPred = 9, // True-motion - NearestMv = 10, - NearMv = 11, - ZeroMv = 12, - NewMv = 13, - MbModeCount = 14, + DcPred, // Average of above and left pixels + VPred, // Vertical + HPred, // Horizontal + D45Pred, // Directional 45 deg = round(arctan(1 / 1) * 180 / pi) + D135Pred, // Directional 135 deg = 180 - 45 + D117Pred, // Directional 117 deg = 180 - 63 + D153Pred, // Directional 153 deg = 180 - 27 + D207Pred, // Directional 207 deg = 180 + 27 + D63Pred, // Directional 63 deg = round(arctan(2 / 1) * 180 / pi) + TmPred, // True-motion + NearestMv, + NearMv, + ZeroMv, + NewMv, + MbModeCount } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/RefBuffer.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/RefBuffer.cs index 0fbb84514..4b45f2c02 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/RefBuffer.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/RefBuffer.cs @@ -2,7 +2,10 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types { internal struct RefBuffer { + public const int InvalidIdx = -1; // Invalid buffer index. + + public int Idx; public Surface Buf; public ScaleFactors Sf; } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/RefCntBuffer.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/RefCntBuffer.cs new file mode 100644 index 000000000..47febf38d --- /dev/null +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/RefCntBuffer.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Graphics.Nvdec.Vp9.Types +{ + internal struct RefCntBuffer + { + public int RefCount; + public int MiRows; + public int MiCols; + public byte Released; + public VpxCodecFrameBuffer RawFrameBuffer; + public Surface Buf; + } +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/ReferenceMode.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/ReferenceMode.cs index aff3cbb07..a678f8181 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/ReferenceMode.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/ReferenceMode.cs @@ -1,10 +1,10 @@ -namespace Ryujinx.Graphics.Nvdec.Vp9.Types +namespace Ryujinx.Graphics.Nvdec.Vp9.Types { internal enum ReferenceMode { - SingleReference = 0, - CompoundReference = 1, - ReferenceModeSelect = 2, - ReferenceModes = 3, + Single, + Compound, + Select, + ReferenceModes } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/ScaleFactors.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/ScaleFactors.cs index 8e2ec4249..212571311 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/ScaleFactors.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/ScaleFactors.cs @@ -1,4 +1,4 @@ -using Ryujinx.Common.Memory; +using Ryujinx.Common.Memory; using System.Runtime.CompilerServices; using static Ryujinx.Graphics.Nvdec.Vp9.Dsp.Convolve; using static Ryujinx.Graphics.Nvdec.Vp9.Dsp.Filter; @@ -8,7 +8,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types internal struct ScaleFactors { private const int RefScaleShift = 14; - private const int RefNoScale = (1 << RefScaleShift); + private const int RefNoScale = 1 << RefScaleShift; private const int RefInvalidScale = -1; private unsafe delegate void ConvolveFn( @@ -38,255 +38,103 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types int h, int bd); - private static readonly unsafe ConvolveFn[][][] _predictX16Y16 = { - new[] - { - new ConvolveFn[] - { - ConvolveCopy, - ConvolveAvg, - }, - new ConvolveFn[] - { - Convolve8Vert, - Convolve8AvgVert, - }, - }, - new[] - { - new ConvolveFn[] - { - Convolve8Horiz, - Convolve8AvgHoriz, - }, - new ConvolveFn[] - { - Convolve8, - Convolve8Avg, - }, - }, - }; + private static readonly unsafe ConvolveFn[][][] _predictX16Y16 = + [ + [ + [ConvolveCopy, ConvolveAvg], + [Convolve8Vert, Convolve8AvgVert] + ], + [ + [Convolve8Horiz, Convolve8AvgHoriz], + [Convolve8, Convolve8Avg] + ] + ]; - private static readonly unsafe ConvolveFn[][][] _predictX16 = { - new[] - { - new ConvolveFn[] - { - ScaledVert, - ScaledAvgVert, - }, - new ConvolveFn[] - { - ScaledVert, - ScaledAvgVert, - }, - }, - new[] - { - new ConvolveFn[] - { - Scaled2D, - ScaledAvg2D, - }, - new ConvolveFn[] - { - Scaled2D, - ScaledAvg2D, - }, - }, - }; + private static readonly unsafe ConvolveFn[][][] _predictX16 = + [ + [ + [ScaledVert, ScaledAvgVert], [ScaledVert, ScaledAvgVert] + ], + [[Scaled2D, ScaledAvg2D], [Scaled2D, ScaledAvg2D]] + ]; - private static readonly unsafe ConvolveFn[][][] _predictY16 = { - new[] - { - new ConvolveFn[] - { - ScaledHoriz, - ScaledAvgHoriz, - }, - new ConvolveFn[] - { - Scaled2D, - ScaledAvg2D, - }, - }, - new[] - { - new ConvolveFn[] - { - ScaledHoriz, - ScaledAvgHoriz, - }, - new ConvolveFn[] - { - Scaled2D, - ScaledAvg2D, - }, - }, - }; + private static readonly unsafe ConvolveFn[][][] _predictY16 = + [ + [[ScaledHoriz, ScaledAvgHoriz], [Scaled2D, ScaledAvg2D]], + [[ScaledHoriz, ScaledAvgHoriz], [Scaled2D, ScaledAvg2D]] + ]; - private static readonly unsafe ConvolveFn[][][] _predict = { - new[] - { - new ConvolveFn[] - { - Scaled2D, - ScaledAvg2D, - }, - new ConvolveFn[] - { - Scaled2D, - ScaledAvg2D, - }, - }, - new[] - { - new ConvolveFn[] - { - Scaled2D, - ScaledAvg2D, - }, - new ConvolveFn[] - { - Scaled2D, - ScaledAvg2D, - }, - }, - }; + private static readonly unsafe ConvolveFn[][][] _predict = + [ + [[Scaled2D, ScaledAvg2D], [Scaled2D, ScaledAvg2D]], + [[Scaled2D, ScaledAvg2D], [Scaled2D, ScaledAvg2D]] + ]; - private static readonly unsafe HighbdConvolveFn[][][] _highbdPredictX16Y16 = { - new[] - { - new HighbdConvolveFn[] - { - HighbdConvolveCopy, - HighbdConvolveAvg, - }, - new HighbdConvolveFn[] - { - HighbdConvolve8Vert, - HighbdConvolve8AvgVert, - }, - }, - new[] - { - new HighbdConvolveFn[] - { - HighbdConvolve8Horiz, - HighbdConvolve8AvgHoriz, - }, - new HighbdConvolveFn[] - { - HighbdConvolve8, - HighbdConvolve8Avg, - }, - }, - }; + private static readonly unsafe HighbdConvolveFn[][][] _highbdPredictX16Y16 = + [ + [ + [HighbdConvolveCopy, HighbdConvolveAvg], + [HighbdConvolve8Vert, HighbdConvolve8AvgVert] + ], + [ + [HighbdConvolve8Horiz, HighbdConvolve8AvgHoriz], + [HighbdConvolve8, HighbdConvolve8Avg] + ] + ]; - private static readonly unsafe HighbdConvolveFn[][][] _highbdPredictX16 = { - new[] - { - new HighbdConvolveFn[] - { - HighbdConvolve8Vert, - HighbdConvolve8AvgVert, - }, - new HighbdConvolveFn[] - { - HighbdConvolve8Vert, - HighbdConvolve8AvgVert, - }, - }, - new[] - { - new HighbdConvolveFn[] - { - HighbdConvolve8, - HighbdConvolve8Avg, - }, - new HighbdConvolveFn[] - { - HighbdConvolve8, - HighbdConvolve8Avg, - }, - }, - }; + private static readonly unsafe HighbdConvolveFn[][][] _highbdPredictX16 = + [ + [ + [HighbdConvolve8Vert, HighbdConvolve8AvgVert], + [HighbdConvolve8Vert, HighbdConvolve8AvgVert] + ], + [ + [HighbdConvolve8, HighbdConvolve8Avg], + [HighbdConvolve8, HighbdConvolve8Avg] + ] + ]; - private static readonly unsafe HighbdConvolveFn[][][] _highbdPredictY16 = { - new[] - { - new HighbdConvolveFn[] - { - HighbdConvolve8Horiz, - HighbdConvolve8AvgHoriz, - }, - new HighbdConvolveFn[] - { - HighbdConvolve8, - HighbdConvolve8Avg, - }, - }, - new[] - { - new HighbdConvolveFn[] - { - HighbdConvolve8Horiz, - HighbdConvolve8AvgHoriz, - }, - new HighbdConvolveFn[] - { - HighbdConvolve8, - HighbdConvolve8Avg, - }, - }, - }; + private static readonly unsafe HighbdConvolveFn[][][] _highbdPredictY16 = + [ + [ + [HighbdConvolve8Horiz, HighbdConvolve8AvgHoriz], + [HighbdConvolve8, HighbdConvolve8Avg] + ], + [ + [HighbdConvolve8Horiz, HighbdConvolve8AvgHoriz], + [HighbdConvolve8, HighbdConvolve8Avg] + ] + ]; - private static readonly unsafe HighbdConvolveFn[][][] _highbdPredict = { - new[] - { - new HighbdConvolveFn[] - { - HighbdConvolve8, - HighbdConvolve8Avg, - }, - new HighbdConvolveFn[] - { - HighbdConvolve8, - HighbdConvolve8Avg, - }, - }, - new[] - { - new HighbdConvolveFn[] - { - HighbdConvolve8, - HighbdConvolve8Avg, - }, - new HighbdConvolveFn[] - { - HighbdConvolve8, - HighbdConvolve8Avg, - }, - }, - }; + private static readonly unsafe HighbdConvolveFn[][][] _highbdPredict = + [ + [ + [HighbdConvolve8, HighbdConvolve8Avg], + [HighbdConvolve8, HighbdConvolve8Avg] + ], + [ + [HighbdConvolve8, HighbdConvolve8Avg], + [HighbdConvolve8, HighbdConvolve8Avg] + ] + ]; - public int XScaleFP; // Horizontal fixed point scale factor - public int YScaleFP; // Vertical fixed point scale factor + public int XScaleFp; // Horizontal fixed point scale factor + public int YScaleFp; // Vertical fixed point scale factor public int XStepQ4; public int YStepQ4; - public readonly int ScaleValueX(int val) + public int ScaleValueX(int val) { return IsScaled() ? ScaledX(val) : val; } - public readonly int ScaleValueY(int val) + public int ScaleValueY(int val) { return IsScaled() ? ScaledY(val) : val; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly unsafe void InterPredict( + public unsafe void InterPredict( int horiz, int vert, int avg, @@ -307,12 +155,14 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types if (YStepQ4 == 16) { // No scaling in either direction. - _predictX16Y16[horiz][vert][avg](src, srcStride, dst, dstStride, kernel, subpelX, xs, subpelY, ys, w, h); + _predictX16Y16[horiz][vert][avg](src, srcStride, dst, dstStride, kernel, subpelX, xs, subpelY, ys, w, + h); } else { // No scaling in x direction. Must always scale in the y direction. - _predictX16[horiz][vert][avg](src, srcStride, dst, dstStride, kernel, subpelX, xs, subpelY, ys, w, h); + _predictX16[horiz][vert][avg](src, srcStride, dst, dstStride, kernel, subpelX, xs, subpelY, ys, w, + h); } } else @@ -320,7 +170,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types if (YStepQ4 == 16) { // No scaling in the y direction. Must always scale in the x direction. - _predictY16[horiz][vert][avg](src, srcStride, dst, dstStride, kernel, subpelX, xs, subpelY, ys, w, h); + _predictY16[horiz][vert][avg](src, srcStride, dst, dstStride, kernel, subpelX, xs, subpelY, ys, w, + h); } else { @@ -331,7 +182,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly unsafe void HighbdInterPredict( + public unsafe void HighbdInterPredict( int horiz, int vert, int avg, @@ -353,12 +204,14 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types if (YStepQ4 == 16) { // No scaling in either direction. - _highbdPredictX16Y16[horiz][vert][avg](src, srcStride, dst, dstStride, kernel, subpelX, xs, subpelY, ys, w, h, bd); + _highbdPredictX16Y16[horiz][vert][avg](src, srcStride, dst, dstStride, kernel, subpelX, xs, subpelY, + ys, w, h, bd); } else { // No scaling in x direction. Must always scale in the y direction. - _highbdPredictX16[horiz][vert][avg](src, srcStride, dst, dstStride, kernel, subpelX, xs, subpelY, ys, w, h, bd); + _highbdPredictX16[horiz][vert][avg](src, srcStride, dst, dstStride, kernel, subpelX, xs, subpelY, ys, + w, h, bd); } } else @@ -366,24 +219,26 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types if (YStepQ4 == 16) { // No scaling in the y direction. Must always scale in the x direction. - _highbdPredictY16[horiz][vert][avg](src, srcStride, dst, dstStride, kernel, subpelX, xs, subpelY, ys, w, h, bd); + _highbdPredictY16[horiz][vert][avg](src, srcStride, dst, dstStride, kernel, subpelX, xs, subpelY, ys, + w, h, bd); } else { // Must always scale in both directions. - _highbdPredict[horiz][vert][avg](src, srcStride, dst, dstStride, kernel, subpelX, xs, subpelY, ys, w, h, bd); + _highbdPredict[horiz][vert][avg](src, srcStride, dst, dstStride, kernel, subpelX, xs, subpelY, ys, w, + h, bd); } } } - private readonly int ScaledX(int val) + private int ScaledX(int val) { - return (int)((long)val * XScaleFP >> RefScaleShift); + return (int)(((long)val * XScaleFp) >> RefScaleShift); } - private readonly int ScaledY(int val) + private int ScaledY(int val) { - return (int)((long)val * YScaleFP >> RefScaleShift); + return (int)(((long)val * YScaleFp) >> RefScaleShift); } private static int GetFixedPointScaleFactor(int otherSize, int thisSize) @@ -399,23 +254,18 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types { int xOffQ4 = ScaledX(x << SubpelBits) & SubpelMask; int yOffQ4 = ScaledY(y << SubpelBits) & SubpelMask; - Mv32 res = new() - { - Row = ScaledY(mv.Row) + yOffQ4, - Col = ScaledX(mv.Col) + xOffQ4, - }; - + Mv32 res = new() { Row = ScaledY(mv.Row) + yOffQ4, Col = ScaledX(mv.Col) + xOffQ4 }; return res; } - public readonly bool IsValidScale() + public bool IsValidScale() { - return XScaleFP != RefInvalidScale && YScaleFP != RefInvalidScale; + return XScaleFp != RefInvalidScale && YScaleFp != RefInvalidScale; } - public readonly bool IsScaled() + public bool IsScaled() { - return IsValidScale() && (XScaleFP != RefNoScale || YScaleFP != RefNoScale); + return IsValidScale() && (XScaleFp != RefNoScale || YScaleFp != RefNoScale); } public static bool ValidRefFrameSize(int refWidth, int refHeight, int thisWidth, int thisHeight) @@ -430,16 +280,15 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types { if (!ValidRefFrameSize(otherW, otherH, thisW, thisH)) { - XScaleFP = RefInvalidScale; - YScaleFP = RefInvalidScale; - + XScaleFp = RefInvalidScale; + YScaleFp = RefInvalidScale; return; } - XScaleFP = GetFixedPointScaleFactor(otherW, thisW); - YScaleFP = GetFixedPointScaleFactor(otherH, thisH); + XScaleFp = GetFixedPointScaleFactor(otherW, thisW); + YScaleFp = GetFixedPointScaleFactor(otherH, thisH); XStepQ4 = ScaledX(16); YStepQ4 = ScaledY(16); } } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/SegLvlFeatures.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/SegLvlFeatures.cs index eb47abdfd..21d57d1d7 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/SegLvlFeatures.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/SegLvlFeatures.cs @@ -1,11 +1,11 @@ -namespace Ryujinx.Graphics.Nvdec.Vp9.Types +namespace Ryujinx.Graphics.Nvdec.Vp9.Types { internal enum SegLvlFeatures { - SegLvlAltQ = 0, // Use alternate Quantizer .... - SegLvlAltLf = 1, // Use alternate loop filter value... - SegLvlRefFrame = 2, // Optional Segment reference frame - SegLvlSkip = 3, // Optional Segment (0,0) + skip mode - SegLvlMax = 4, // Number of features supported + AltQ, // Use alternate Quantizer .... + AltLf, // Use alternate loop filter value... + RefFrame, // Optional Segment reference frame + Skip, // Optional Segment (0,0) + skip mode + Max // Number of features supported } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Segmentation.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Segmentation.cs index f2415fc09..726476d96 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Segmentation.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Segmentation.cs @@ -1,4 +1,6 @@ -using Ryujinx.Common.Memory; +using Ryujinx.Common.Memory; +using Ryujinx.Graphics.Video; +using System; using System.Diagnostics; using System.Runtime.InteropServices; @@ -6,8 +8,16 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types { internal struct Segmentation { - private static readonly int[] _segFeatureDataSigned = { 1, 1, 0, 0 }; - private static readonly int[] _segFeatureDataMax = { QuantCommon.MaxQ, Vp9.LoopFilter.MaxLoopFilter, 3, 0 }; + public const int SegmentDeltadata = 0; + public const int SegmentAbsdata = 1; + + public const int MaxSegments = 8; + public const int SegTreeProbs = MaxSegments - 1; + + public const int PredictionProbs = 3; + + private static readonly int[] _segFeatureDataSigned = [1, 1, 0, 0]; + private static readonly int[] _segFeatureDataMax = [QuantCommon.MaxQ, Vp9.LoopFilter.MaxLoopFilter, 3, 0]; public bool Enabled; public bool UpdateMap; @@ -67,5 +77,88 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types { return FeatureData[segmentId][(int)featureId]; } + + public int GetQIndex(int segmentId, int baseQIndex) + { + if (IsSegFeatureActive(segmentId, SegLvlFeatures.AltQ) != 0) + { + int data = GetSegData(segmentId, SegLvlFeatures.AltQ); + int segQIndex = AbsDelta == Constants.SegmentAbsData ? data : baseQIndex + data; + return Math.Clamp(segQIndex, 0, QuantCommon.MaxQ); + } + + return baseQIndex; + } + + public void SetupSegmentation(ref Vp9EntropyProbs fc, ref ReadBitBuffer rb) + { + UpdateMap = false; + UpdateData = 0; + + Enabled = rb.ReadBit() != 0; + if (!Enabled) + { + return; + } + + // Segmentation map update + UpdateMap = rb.ReadBit() != 0; + if (UpdateMap) + { + for (int i = 0; i < SegTreeProbs; i++) + { + fc.SegTreeProb[i] = rb.ReadBit() != 0 + ? (byte)rb.ReadLiteral(8) + : (byte)Prob.MaxProb; + } + + TemporalUpdate = rb.ReadBit() != 0; + if (TemporalUpdate) + { + for (int i = 0; i < PredictionProbs; i++) + { + fc.SegPredProb[i] = rb.ReadBit() != 0 + ? (byte)rb.ReadLiteral(8) + : (byte)Prob.MaxProb; + } + } + else + { + for (int i = 0; i < PredictionProbs; i++) + { + fc.SegPredProb[i] = Prob.MaxProb; + } + } + } + + // Segmentation data update + UpdateData = (byte)rb.ReadBit(); + if (UpdateData != 0) + { + AbsDelta = (byte)rb.ReadBit(); + + ClearAllSegFeatures(); + + for (int i = 0; i < Constants.MaxSegments; i++) + { + for (int j = 0; j < (int)SegLvlFeatures.Max; j++) + { + int data = 0; + int featureEnabled = rb.ReadBit(); + if (featureEnabled != 0) + { + EnableSegFeature(i, (SegLvlFeatures)j); + data = rb.DecodeUnsignedMax(FeatureDataMax((SegLvlFeatures)j)); + if (IsSegFeatureSigned((SegLvlFeatures)j) != 0) + { + data = rb.ReadBit() != 0 ? -data : data; + } + } + + SetSegData(i, (SegLvlFeatures)j, data); + } + } + } + } } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Surface.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Surface.cs index 6bab536d1..33741f85f 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Surface.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Surface.cs @@ -1,11 +1,23 @@ -using Ryujinx.Common.Memory; +using Ryujinx.Common.Memory; +using Ryujinx.Graphics.Nvdec.Vp9.Common; using Ryujinx.Graphics.Video; +using System.Diagnostics; using System.Runtime.InteropServices; namespace Ryujinx.Graphics.Nvdec.Vp9.Types { + internal delegate int VpxGetFrameBufferCbFnT(MemoryAllocator allocator, Ptr cbPriv, + ulong minSize, ref VpxCodecFrameBuffer fb); + internal struct Surface : ISurface { + public const int Innerborderinpixels = 96; + public const int InterpExtend = 4; + public const int EncBorderInPixels = 160; + public const int DecBorderInPixels = 32; + + public const int Yv12FlagHighbitdepth = 8; + public ArrayPtr YBuffer; public ArrayPtr UBuffer; public ArrayPtr VBuffer; @@ -14,43 +26,62 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types public readonly unsafe Plane UPlane => new((nint)UBuffer.ToPointer(), UBuffer.Length); public readonly unsafe Plane VPlane => new((nint)VBuffer.ToPointer(), VBuffer.Length); - public readonly FrameField Field => FrameField.Progressive; + public FrameField Field => FrameField.Progressive; - public int Width { get; } - public int Height { get; } - public int AlignedWidth { get; } - public int AlignedHeight { get; } - public int Stride { get; } - public int UvWidth { get; } - public int UvHeight { get; } - public int UvAlignedWidth { get; } - public int UvAlignedHeight { get; } - public int UvStride { get; } + public int Width { get; private set; } + public int Height { get; private set; } + public int AlignedWidth { get; private set; } + public int AlignedHeight { get; private set; } + public int Stride { get; private set; } + public int UvWidth { get; private set; } + public int UvHeight { get; private set; } + public int UvAlignedWidth { get; private set; } + public int UvAlignedHeight { get; private set; } + public int UvStride { get; private set; } + public bool HighBd { get; private set; } - public bool HighBd { get; } + public int FrameSize { get; private set; } + public int Border { get; private set; } + + public int YCropWidth => Width; + public int YCropHeight => Height; + public int UvCropWidth => UvWidth; + public int UvCropHeight => UvHeight; + + public ArrayPtr BufferAlloc; + public int BufferAllocSz; + public int SubsamplingX; + public int SubsamplingY; + public uint BitDepth; + public VpxColorSpace ColorSpace; + public VpxColorRange ColorRange; + public int RenderWidth; + public int RenderHeight; + + public int Corrupted; + public int Flags; private readonly nint _pointer; public Surface(int width, int height) { - HighBd = false; - const int Border = 32; const int SsX = 1; const int SsY = 1; + const bool Highbd = false; int alignedWidth = (width + 7) & ~7; int alignedHeight = (height + 7) & ~7; - int yStride = ((alignedWidth + 2 * Border) + 31) & ~31; - int yplaneSize = (alignedHeight + 2 * Border) * yStride; + int yStride = (alignedWidth + (2 * Border) + 31) & ~31; + int yplaneSize = (alignedHeight + (2 * Border)) * yStride; int uvWidth = alignedWidth >> SsX; int uvHeight = alignedHeight >> SsY; int uvStride = yStride >> SsX; int uvBorderW = Border >> SsX; int uvBorderH = Border >> SsY; - int uvplaneSize = (uvHeight + 2 * uvBorderH) * uvStride; + int uvplaneSize = (uvHeight + (2 * uvBorderH)) * uvStride; - int frameSize = (HighBd ? 2 : 1) * (yplaneSize + 2 * uvplaneSize); + int frameSize = (Highbd ? 2 : 1) * (yplaneSize + (2 * uvplaneSize)); nint pointer = Marshal.AllocHGlobal(frameSize); _pointer = pointer; @@ -65,9 +96,9 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types UvAlignedHeight = uvHeight; UvStride = uvStride; - ArrayPtr NewPlane(int start, int size, int planeBorder) + ArrayPtr NewPlane(int start, int size, int border) { - return new ArrayPtr(pointer + start + planeBorder, size - planeBorder); + return new ArrayPtr(pointer + start + border, size - border); } YBuffer = NewPlane(0, yplaneSize, (Border * yStride) + Border); @@ -75,7 +106,132 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types VBuffer = NewPlane(yplaneSize + uvplaneSize, uvplaneSize, (uvBorderH * uvStride) + uvBorderW); } - public readonly void Dispose() + public unsafe int ReallocFrameBuffer( + MemoryAllocator allocator, + int width, + int height, + int ssX, + int ssY, + bool useHighbitdepth, + int border, + int byteAlignment, + Ptr fb, + VpxGetFrameBufferCbFnT cb, + Ptr cbPriv) + { + int byteAlign = byteAlignment == 0 ? 1 : byteAlignment; // TODO: Is it safe to ignore the alignment? + int alignedWidth = (width + 7) & ~7; + int alignedHeight = (height + 7) & ~7; + int yStride = (alignedWidth + (2 * border) + 31) & ~31; + ulong yplaneSize = + ((ulong)(alignedHeight + (2 * border)) * (ulong)yStride) + (ulong)byteAlignment; + int uvWidth = alignedWidth >> ssX; + int uvHeight = alignedHeight >> ssY; + int uvStride = yStride >> ssX; + int uvBorderW = border >> ssX; + int uvBorderH = border >> ssY; + ulong uvplaneSize = + ((ulong)(uvHeight + (2 * uvBorderH)) * (ulong)uvStride) + (ulong)byteAlignment; + + ulong frameSize = (ulong)(1 + (useHighbitdepth ? 1 : 0)) * (yplaneSize + (2 * uvplaneSize)); + + ArrayPtr buf = ArrayPtr.Null; + + // frame_size is stored in buffer_alloc_sz, which is an int. If it won't + // fit, fail early. + if (frameSize > int.MaxValue) + { + return -1; + } + + if (cb != null) + { + const int AlignAddrExtraSize = 31; + ulong externalFrameSize = frameSize + AlignAddrExtraSize; + + Debug.Assert(!fb.IsNull); + + // Allocation to hold larger frame, or first allocation. + if (cb(allocator, cbPriv, externalFrameSize, ref fb.Value) < 0) + { + return -1; + } + + if (fb.Value.Data.IsNull || (ulong)fb.Value.Data.Length < externalFrameSize) + { + return -1; + } + + BufferAlloc = fb.Value.Data; + } + else if (frameSize > (ulong)BufferAllocSz) + { + // Allocation to hold larger frame, or first allocation. + allocator.Free(BufferAlloc); + BufferAlloc = ArrayPtr.Null; + + BufferAlloc = allocator.Allocate((int)frameSize); + if (BufferAlloc.IsNull) + { + return -1; + } + + BufferAllocSz = (int)frameSize; + + // This memset is needed for fixing valgrind error from C loop filter + // due to access uninitialized memory in frame border. It could be + // removed if border is totally removed. + MemoryUtil.Fill(BufferAlloc.ToPointer(), (byte)0, BufferAllocSz); + } + + /* Only support allocating buffers that have a border that's a multiple + * of 32. The border restriction is required to get 16-byte alignment of + * the start of the chroma rows without introducing an arbitrary gap + * between planes, which would break the semantics of things like + * vpx_img_set_rect(). */ + if ((border & 0x1f) != 0) + { + return -3; + } + + Width = width; + Height = height; + AlignedWidth = alignedWidth; + AlignedHeight = alignedHeight; + Stride = yStride; + + UvWidth = (width + ssX) >> ssX; + UvHeight = (height + ssY) >> ssY; + UvAlignedWidth = uvWidth; + UvAlignedHeight = uvHeight; + UvStride = uvStride; + + Border = border; + FrameSize = (int)frameSize; + SubsamplingX = ssX; + SubsamplingY = ssY; + + buf = BufferAlloc; + if (useHighbitdepth) + { + // Store uint16 addresses when using 16bit framebuffers + buf = BufferAlloc; + Flags = Yv12FlagHighbitdepth; + } + else + { + Flags = 0; + } + + YBuffer = buf.Slice((border * yStride) + border); + UBuffer = buf.Slice((int)yplaneSize + (uvBorderH * uvStride) + uvBorderW); + VBuffer = buf.Slice((int)yplaneSize + (int)uvplaneSize + (uvBorderH * uvStride) + uvBorderW); + + Corrupted = 0; /* assume not corrupted by errors */ + return 0; + } + + public void Dispose() { Marshal.FreeHGlobal(_pointer); } diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/TileInfo.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/TileInfo.cs index a3eea81a2..7d4350d3c 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/TileInfo.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/TileInfo.cs @@ -56,7 +56,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types private static int GetMinLog2TileCols(int sb64Cols) { int minLog2 = 0; - while ((MaxTileWidthB64 << minLog2) < sb64Cols) + while (MaxTileWidthB64 << minLog2 < sb64Cols) { ++minLog2; } @@ -67,7 +67,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types private static int GetMaxLog2TileCols(int sb64Cols) { int maxLog2 = 1; - while ((sb64Cols >> maxLog2) >= MinTileWidthB64) + while (sb64Cols >> maxLog2 >= MinTileWidthB64) { ++maxLog2; } @@ -75,7 +75,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types return maxLog2 - 1; } - public static void GetTileNBits(int miCols, ref int minLog2TileCols, ref int maxLog2TileCols) + public static void GetTileNBits(int miCols, out int minLog2TileCols, out int maxLog2TileCols) { int sb64Cols = MiColsAlignedToSb(miCols) >> Constants.MiBlockSizeLog2; minLog2TileCols = GetMinLog2TileCols(sb64Cols); @@ -83,4 +83,4 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types Debug.Assert(minLog2TileCols <= maxLog2TileCols); } } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/TxMode.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/TxMode.cs index b3a05ed9a..d6a4fb843 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/TxMode.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/TxMode.cs @@ -1,12 +1,12 @@ -namespace Ryujinx.Graphics.Nvdec.Vp9.Types +namespace Ryujinx.Graphics.Nvdec.Vp9.Types { public enum TxMode { - Only4X4 = 0, // Only 4x4 transform used - Allow8X8 = 1, // Allow block transform size up to 8x8 - Allow16X16 = 2, // Allow block transform size up to 16x16 - Allow32X32 = 3, // Allow block transform size up to 32x32 - TxModeSelect = 4, // Transform specified for each block - TxModes = 5, + Only4X4, // Only 4x4 transform used + Allow8X8, // Allow block transform size up to 8x8 + Allow16X16, // Allow block transform size up to 16x16 + Allow32X32, // Allow block transform size up to 32x32 + TxModeSelect, // Transform specified for each block + TxModes } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/TxSize.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/TxSize.cs index 0342087b8..72908a00c 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/TxSize.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/TxSize.cs @@ -1,11 +1,11 @@ -namespace Ryujinx.Graphics.Nvdec.Vp9.Types +namespace Ryujinx.Graphics.Nvdec.Vp9.Types { public enum TxSize { - Tx4x4 = 0, // 4x4 transform - Tx8x8 = 1, // 8x8 transform - Tx16x16 = 2, // 16x16 transform - Tx32x32 = 3, // 32x32 transform - TxSizes = 4, + Tx4X4, // 4x4 transform + Tx8X8, // 8x8 transform + Tx16X16, // 16x16 transform + Tx32X32, // 32x32 transform + TxSizes } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/TxType.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/TxType.cs index e7b37c925..60c5c9a44 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/TxType.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/TxType.cs @@ -1,11 +1,11 @@ -namespace Ryujinx.Graphics.Nvdec.Vp9.Types +namespace Ryujinx.Graphics.Nvdec.Vp9.Types { internal enum TxType { - DctDct = 0, // DCT in both horizontal and vertical - AdstDct = 1, // ADST in vertical, DCT in horizontal - DctAdst = 2, // DCT in vertical, ADST in horizontal - AdstAdst = 3, // ADST in both directions - TxTypes = 4, + DctDct, // DCT in both horizontal and vertical + AdstDct, // ADST in vertical, DCT in horizontal + DctAdst, // DCT in vertical, ADST in horizontal + AdstAdst, // ADST in both directions + TxTypes } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Vp9Common.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Vp9Common.cs index 2456e9068..5669e8574 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Vp9Common.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Vp9Common.cs @@ -1,6 +1,8 @@ -using Ryujinx.Common.Memory; +using Ryujinx.Common.Memory; using Ryujinx.Graphics.Nvdec.Vp9.Common; +using Ryujinx.Graphics.Nvdec.Vp9.Dsp; using Ryujinx.Graphics.Video; +using System; namespace Ryujinx.Graphics.Nvdec.Vp9.Types { @@ -9,27 +11,62 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types public MacroBlockD Mb; public ArrayPtr TileWorkerData; + public int TotalTiles; public InternalErrorInfo Error; + public VpxColorSpace ColorSpace; + public VpxColorRange ColorRange; + public int Width; public int Height; + public int RenderWidth; + public int RenderHeight; + + public int LastWidth; + public int LastHeight; + public int SubsamplingX; public int SubsamplingY; + public bool UseHighBitDepth; + public ArrayPtr PrevFrameMvs; public ArrayPtr CurFrameMvs; + public Ptr FrameToShow; + public Ptr PrevFrame; + + public Ptr CurFrame; + + public Array8 RefFrameMap; /* maps fb_idx to reference slot */ + + // Prepare ref_frame_map for the next frame. + // Only used in frame parallel decode. + public Array8 NextRefFrameMap; + public Array3 FrameRefs; + public int NewFbIdx; + + public int CurShowFrameFbIdx; + + public FrameType LastFrameType; public FrameType FrameType; + public int ShowFrame; + public int LastShowFrame; + public int ShowExistingFrame; + // Flag signaling that the frame is encoded using only Intra modes. public bool IntraOnly; + public bool LastIntraOnly; public bool AllowHighPrecisionMv; + public int ResetFrameContext; + // MBs, MbRows/Cols is in 16-pixel units; MiRows/Cols is in // ModeInfo (8-pixel) units. public int MBs; @@ -49,8 +86,13 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types /* We allocate a ModeInfo struct for each macroblock, together with an extra row on top and column on the left to simplify prediction. */ + public int MiAllocSize; public ArrayPtr Mip; /* Base of allocated array */ - public ArrayPtr Mi; /* Corresponds to upper left visible macroblock */ + public ArrayPtr Mi; /* Corresponds to upper left visible macroblock */ + + // prev_mip and prev_mi will only be allocated in VP9 encoder. + public Ptr PrevMip; /* MODE_INFO array 'mip' from last decoded frame */ + public Ptr PrevMi; /* 'mi' from last frame (points into prev_mip) */ public ArrayPtr> MiGridBase; public ArrayPtr> MiGridVisible; @@ -70,6 +112,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types public LoopFilterInfoN LfInfo; + public int RefreshFrameContext; /* Two state 0 = NO, 1 = YES */ + public Array4 RefFrameSignBias; /* Two state 0, 1 */ public LoopFilter Lf; @@ -81,22 +125,37 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types public ReferenceMode ReferenceMode; public Ptr Fc; + public ArrayPtr FrameContexts; // FRAME_CONTEXTS + public uint FrameContextIdx; /* Context to use/update */ public Ptr Counts; + public uint CurrentVideoFrame; + public BitstreamProfile Profile; + + public BitDepth BitDepth; + public BitDepth DequantBitDepth; // bit_depth of current dequantizer + + public int ErrorResilientMode; + public int FrameParallelDecodingMode; + public int Log2TileCols, Log2TileRows; + public int ByteAlignment; + public int SkipLoopFilter; + + public Ptr BufferPool; + public ArrayPtr AboveSegContext; public ArrayPtr AboveContext; - public readonly bool FrameIsIntraOnly() + public bool FrameIsIntraOnly() { return FrameType == FrameType.KeyFrame || IntraOnly; } public bool CompoundReferenceAllowed() { - int i; - for (i = 1; i < Constants.RefsPerFrame; ++i) + for (int i = 1; i < Constants.RefsPerFrame; ++i) { if (RefFrameSignBias[i + 1] != RefFrameSignBias[1]) { @@ -107,6 +166,47 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types return false; } + public ref Surface GetFrameNewBuffer() + { + return ref BufferPool.Value.FrameBufs[NewFbIdx].Buf; + } + + public int GetFreeFb() + { + ref Array12 frameBufs = ref BufferPool.Value.FrameBufs; + + int i; + + for (i = 0; i < Constants.FrameBuffers; ++i) + { + if (frameBufs[i].RefCount == 0) + { + break; + } + } + + if (i != Constants.FrameBuffers) + { + frameBufs[i].RefCount = 1; + } + else + { + // Reset i to be INVALID_IDX to indicate no free buffer found. + i = RefBuffer.InvalidIdx; + } + + return i; + } + + public void SwapCurrentAndLastSegMap() + { + // Swap indices. + (SegMapIdx, PrevSegMapIdx) = (PrevSegMapIdx, SegMapIdx); + + CurrentFrameSegMap = SegMapArray[SegMapIdx]; + LastFrameSegMap = SegMapArray[PrevSegMapIdx]; + } + private static int CalcMiSize(int len) { // Len is in mi units. @@ -129,19 +229,18 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types public void AllocTileWorkerData(MemoryAllocator allocator, int tileCols, int tileRows, int maxThreads) { - TileWorkerData = allocator.Allocate(tileCols * tileRows + (maxThreads > 1 ? maxThreads : 0)); + TileWorkerData = + allocator.Allocate((tileCols * tileRows) + (maxThreads > 1 ? maxThreads : 0)); } - public readonly void FreeTileWorkerData(MemoryAllocator allocator) + public void FreeTileWorkerData(MemoryAllocator allocator) { allocator.Free(TileWorkerData); } private void AllocSegMap(MemoryAllocator allocator, int segMapSize) { - int i; - - for (i = 0; i < Constants.NumPingPongBuffers; ++i) + for (int i = 0; i < Constants.NumPingPongBuffers; ++i) { SegMapArray[i] = allocator.Allocate(segMapSize); } @@ -156,9 +255,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types private void FreeSegMap(MemoryAllocator allocator) { - int i; - - for (i = 0; i < Constants.NumPingPongBuffers; ++i) + for (int i = 0; i < Constants.NumPingPongBuffers; ++i) { allocator.Free(SegMapArray[i]); SegMapArray[i] = ArrayPtr.Null; @@ -194,6 +291,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types Lf.Lfm = ArrayPtr.Null; allocator.Free(CurFrameMvs); CurFrameMvs = ArrayPtr.Null; + if (UsePrevFrameMvs) { allocator.Free(PrevFrameMvs); @@ -209,7 +307,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types Lf.Lfm = allocator.Allocate(((MiRows + (Constants.MiBlockSize - 1)) >> 3) * Lf.LfmStride); } - public void AllocContextBuffers(MemoryAllocator allocator, int width, int height) + public bool AllocContextBuffers(MemoryAllocator allocator, int width, int height) { SetMbMi(width, height); int newMiSize = MiStride * CalcMiSize(MiRows); @@ -239,6 +337,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types { PrevFrameMvs = allocator.Allocate(MiRows * MiCols); } + + return false; } private unsafe void DecSetupMi() @@ -257,7 +357,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types } } - private readonly void SetPartitionProbs(ref MacroBlockD xd) + private void SetPartitionProbs(ref MacroBlockD xd) { xd.PartitionProbs = FrameIsIntraOnly() ? new ArrayPtr>(ref Fc.Value.KfPartitionProb[0], 16) @@ -266,9 +366,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types internal void InitMacroBlockD(ref MacroBlockD xd, ArrayPtr dqcoeff) { - int i; - - for (i = 0; i < Constants.MaxMbPlane; ++i) + for (int i = 0; i < Constants.MaxMbPlane; ++i) { xd.Plane[i].DqCoeff = dqcoeff; xd.AboveContext[i] = AboveContext.Slice(i * 2 * TileInfo.MiColsAlignedToSb(MiCols)); @@ -281,6 +379,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types { MemoryUtil.Copy(ref xd.Plane[i].SegDequant, ref UvDequant); } + xd.Fc = new Ptr(ref Fc.Value); } @@ -293,29 +392,27 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types public void SetupSegmentationDequant() { - const BitDepth BitDepth = BitDepth.Bits8; // TODO: Configurable // Build y/uv dequant values based on segmentation. if (Seg.Enabled) { - int i; - for (i = 0; i < Constants.MaxSegments; ++i) + for (int i = 0; i < Constants.MaxSegments; ++i) { - int qIndex = QuantCommon.GetQIndex(ref Seg, i, BaseQindex); - YDequant[i][0] = QuantCommon.DcQuant(qIndex, YDcDeltaQ, BitDepth); - YDequant[i][1] = QuantCommon.AcQuant(qIndex, 0, BitDepth); - UvDequant[i][0] = QuantCommon.DcQuant(qIndex, UvDcDeltaQ, BitDepth); - UvDequant[i][1] = QuantCommon.AcQuant(qIndex, UvAcDeltaQ, BitDepth); + int qindex = Seg.GetQIndex(i, BaseQindex); + YDequant[i][0] = QuantCommon.DcQuant(qindex, YDcDeltaQ, BitDepth); + YDequant[i][1] = QuantCommon.AcQuant(qindex, 0, BitDepth); + UvDequant[i][0] = QuantCommon.DcQuant(qindex, UvDcDeltaQ, BitDepth); + UvDequant[i][1] = QuantCommon.AcQuant(qindex, UvAcDeltaQ, BitDepth); } } else { - int qIndex = BaseQindex; + int qindex = BaseQindex; // When segmentation is disabled, only the first value is used. The // remaining are don't cares. - YDequant[0][0] = QuantCommon.DcQuant(qIndex, YDcDeltaQ, BitDepth); - YDequant[0][1] = QuantCommon.AcQuant(qIndex, 0, BitDepth); - UvDequant[0][0] = QuantCommon.DcQuant(qIndex, UvDcDeltaQ, BitDepth); - UvDequant[0][1] = QuantCommon.AcQuant(qIndex, UvAcDeltaQ, BitDepth); + YDequant[0][0] = QuantCommon.DcQuant(qindex, YDcDeltaQ, BitDepth); + YDequant[0][1] = QuantCommon.AcQuant(qindex, 0, BitDepth); + UvDequant[0][0] = QuantCommon.DcQuant(qindex, UvDcDeltaQ, BitDepth); + UvDequant[0][1] = QuantCommon.AcQuant(qindex, UvAcDeltaQ, BitDepth); } } @@ -327,5 +424,576 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types refBuf.Sf.SetupScaleFactorsForFrame(refBuf.Buf.Width, refBuf.Buf.Height, Width, Height); } } + + public void ReadFrameReferenceModeProbs(ref Reader r) + { + ref Vp9EntropyProbs fc = ref Fc.Value; + + + if (ReferenceMode == ReferenceMode.Select) + { + for (int i = 0; i < Constants.CompInterContexts; ++i) + { + r.DiffUpdateProb(ref fc.CompInterProb[i]); + } + } + + if (ReferenceMode != ReferenceMode.Compound) + { + for (int i = 0; i < Constants.RefContexts; ++i) + { + r.DiffUpdateProb(ref fc.SingleRefProb[i][0]); + r.DiffUpdateProb(ref fc.SingleRefProb[i][1]); + } + } + + if (ReferenceMode != ReferenceMode.Single) + { + for (int i = 0; i < Constants.RefContexts; ++i) + { + r.DiffUpdateProb(ref fc.CompRefProb[i]); + } + } + } + + public ReferenceMode ReadFrameReferenceMode(ref Reader r) + { + if (CompoundReferenceAllowed()) + { + return r.ReadBit() != 0 + ? r.ReadBit() != 0 ? ReferenceMode.Select : ReferenceMode.Compound + : ReferenceMode.Single; + } + + return ReferenceMode.Single; + } + + public void SetupCompoundReferenceMode() + { + if (RefFrameSignBias[Constants.LastFrame] == RefFrameSignBias[Constants.GoldenFrame]) + { + CompFixedRef = Constants.AltRefFrame; + CompVarRef[0] = Constants.LastFrame; + CompVarRef[1] = Constants.GoldenFrame; + } + else if (RefFrameSignBias[Constants.LastFrame] == RefFrameSignBias[Constants.AltRefFrame]) + { + CompFixedRef = Constants.GoldenFrame; + CompVarRef[0] = Constants.LastFrame; + CompVarRef[1] = Constants.AltRefFrame; + } + else + { + CompFixedRef = Constants.LastFrame; + CompVarRef[0] = Constants.GoldenFrame; + CompVarRef[1] = Constants.AltRefFrame; + } + } + + public void InitMvProbs() + { + Fc.Value.Joints[0] = 32; + Fc.Value.Joints[1] = 64; + Fc.Value.Joints[2] = 96; + + Fc.Value.Sign[0] = 128; + Fc.Value.Classes[0][0] = 224; + Fc.Value.Classes[0][1] = 144; + Fc.Value.Classes[0][2] = 192; + Fc.Value.Classes[0][3] = 168; + Fc.Value.Classes[0][4] = 192; + Fc.Value.Classes[0][5] = 176; + Fc.Value.Classes[0][6] = 192; + Fc.Value.Classes[0][7] = 198; + Fc.Value.Classes[0][8] = 198; + Fc.Value.Classes[0][9] = 245; + Fc.Value.Class0[0][0] = 216; + Fc.Value.Bits[0][0] = 136; + Fc.Value.Bits[0][1] = 140; + Fc.Value.Bits[0][2] = 148; + Fc.Value.Bits[0][3] = 160; + Fc.Value.Bits[0][4] = 176; + Fc.Value.Bits[0][5] = 192; + Fc.Value.Bits[0][6] = 224; + Fc.Value.Bits[0][7] = 234; + Fc.Value.Bits[0][8] = 234; + Fc.Value.Bits[0][9] = 240; + Fc.Value.Class0Fp[0][0][0] = 128; + Fc.Value.Class0Fp[0][0][1] = 128; + Fc.Value.Class0Fp[0][0][2] = 64; + Fc.Value.Class0Fp[0][1][0] = 96; + Fc.Value.Class0Fp[0][1][1] = 112; + Fc.Value.Class0Fp[0][1][2] = 64; + Fc.Value.Fp[0][0] = 64; + Fc.Value.Fp[0][1] = 96; + Fc.Value.Fp[0][2] = 64; + Fc.Value.Class0Hp[0] = 160; + Fc.Value.Hp[0] = 128; + + Fc.Value.Sign[1] = 128; + Fc.Value.Classes[1][0] = 216; + Fc.Value.Classes[1][1] = 128; + Fc.Value.Classes[1][2] = 176; + Fc.Value.Classes[1][3] = 160; + Fc.Value.Classes[1][4] = 176; + Fc.Value.Classes[1][5] = 176; + Fc.Value.Classes[1][6] = 192; + Fc.Value.Classes[1][7] = 198; + Fc.Value.Classes[1][8] = 198; + Fc.Value.Classes[1][9] = 208; + Fc.Value.Class0[1][0] = 208; + Fc.Value.Bits[1][0] = 136; + Fc.Value.Bits[1][1] = 140; + Fc.Value.Bits[1][2] = 148; + Fc.Value.Bits[1][3] = 160; + Fc.Value.Bits[1][4] = 176; + Fc.Value.Bits[1][5] = 192; + Fc.Value.Bits[1][6] = 224; + Fc.Value.Bits[1][7] = 234; + Fc.Value.Bits[1][8] = 234; + Fc.Value.Bits[1][9] = 240; + Fc.Value.Class0Fp[1][0][0] = 128; + Fc.Value.Class0Fp[1][0][1] = 128; + Fc.Value.Class0Fp[1][0][2] = 64; + Fc.Value.Class0Fp[1][1][0] = 96; + Fc.Value.Class0Fp[1][1][1] = 112; + Fc.Value.Class0Fp[1][1][2] = 64; + Fc.Value.Fp[1][0] = 64; + Fc.Value.Fp[1][1] = 96; + Fc.Value.Fp[1][2] = 64; + Fc.Value.Class0Hp[1] = 160; + Fc.Value.Hp[1] = 128; + } + + public void AdaptMvProbs(bool allowHp) + { + ref Vp9EntropyProbs fc = ref Fc.Value; + ref Vp9EntropyProbs preFc = ref FrameContexts[(int)FrameContextIdx]; + ref Vp9BackwardUpdates counts = ref Counts.Value; + + Prob.VpxTreeMergeProbs( + EntropyMv.JointTree, + preFc.Joints.AsSpan(), + counts.Joints.AsSpan(), + fc.Joints.AsSpan()); + + for (int i = 0; i < 2; ++i) + { + fc.Sign[i] = Prob.ModeMvMergeProbs(preFc.Sign[i], ref counts.Sign[i]); + Prob.VpxTreeMergeProbs( + EntropyMv.ClassTree, + preFc.Classes[i].AsSpan(), + counts.Classes[i].AsSpan(), + fc.Classes[i].AsSpan()); + Prob.VpxTreeMergeProbs( + EntropyMv.Class0Tree, + preFc.Class0[i].AsSpan(), + counts.Class0[i].AsSpan(), + fc.Class0[i].AsSpan()); + + for (int j = 0; j < EntropyMv.OffsetBits; ++j) + { + fc.Bits[i][j] = Prob.ModeMvMergeProbs(preFc.Bits[i][j], ref counts.Bits[i][j]); + } + + for (int j = 0; j < EntropyMv.Class0Size; ++j) + { + Prob.VpxTreeMergeProbs( + EntropyMv.FpTree, + preFc.Class0Fp[i][j].AsSpan(), + counts.Class0Fp[i][j].AsSpan(), + fc.Class0Fp[i][j].AsSpan()); + } + + Prob.VpxTreeMergeProbs(EntropyMv.FpTree, preFc.Fp[i].AsSpan(), counts.Fp[i].AsSpan(), + fc.Fp[i].AsSpan()); + + if (allowHp) + { + fc.Class0Hp[i] = Prob.ModeMvMergeProbs(preFc.Class0Hp[i], ref counts.Class0Hp[i]); + fc.Hp[i] = Prob.ModeMvMergeProbs(preFc.Hp[i], ref counts.Hp[i]); + } + } + } + + public void ResizeContextBuffers(MemoryAllocator allocator, int width, int height) + { + if (Width != width || Height != height) + { + int newMiRows = BitUtils.AlignPowerOfTwo(height, Constants.MiSizeLog2) >> Constants.MiSizeLog2; + int newMiCols = BitUtils.AlignPowerOfTwo(width, Constants.MiSizeLog2) >> Constants.MiSizeLog2; + + // Allocations in AllocContextBuffers() depend on individual + // dimensions as well as the overall size. + if (newMiCols > MiCols || newMiRows > MiRows) + { + if (AllocContextBuffers(allocator, width, height)) + { + // The Mi* values have been cleared and any existing context + // buffers have been freed. Clear Width and Height to be + // consistent and to force a realloc next time. + Width = 0; + Height = 0; + Error.InternalError(CodecErr.MemError, "Failed to allocate context buffers"); + } + } + else + { + SetMbMi(width, height); + } + + InitContextBuffers(); + Width = width; + Height = height; + } + + if (CurFrameMvs.IsNull || + MiRows > CurFrame.Value.MiRows || + MiCols > CurFrame.Value.MiCols) + { + ResizeMvBuffer(allocator); + } + } + + public void CheckMemError(ref ArrayPtr lval, ArrayPtr expr) + where T : unmanaged + { + lval = expr; + if (lval.IsNull) + { + Error.InternalError(CodecErr.MemError, "Failed to allocate"); + } + } + + private void ResizeMvBuffer(MemoryAllocator allocator) + { + allocator.Free(CurFrameMvs); + CurFrame.Value.MiRows = MiRows; + CurFrame.Value.MiCols = MiCols; + CheckMemError(ref CurFrameMvs, allocator.Allocate(MiRows * MiCols)); + } + + public void CheckMemError(ref Ptr lval, Ptr expr) where T : unmanaged + { + lval = expr; + if (lval.IsNull) + { + Error.InternalError(CodecErr.MemError, "Failed to allocate"); + } + } + + public void SetupTileInfo(ref ReadBitBuffer rb) + { + int minLog2TileCols = 0, maxLog2TileCols = 0, maxOnes; + TileInfo.GetTileNBits(MiCols, out minLog2TileCols, out maxLog2TileCols); + + // columns + maxOnes = maxLog2TileCols - minLog2TileCols; + Log2TileCols = minLog2TileCols; + while (maxOnes-- != 0 && rb.ReadBit() != 0) + { + Log2TileCols++; + } + + if (Log2TileCols > 6) + { + Error.InternalError(CodecErr.CorruptFrame, "Invalid number of tile columns"); + } + + // rows + Log2TileRows = rb.ReadBit(); + if (Log2TileRows != 0) + { + Log2TileRows += rb.ReadBit(); + } + } + + public void ReadBitdepthColorspaceSampling(ref ReadBitBuffer rb) + { + if (Profile >= BitstreamProfile.Profile2) + { + BitDepth = rb.ReadBit() != 0 ? BitDepth.Bits12 : BitDepth.Bits10; + UseHighBitDepth = true; + } + else + { + BitDepth = BitDepth.Bits8; + UseHighBitDepth = false; + } + + ColorSpace = (VpxColorSpace)rb.ReadLiteral(3); + if (ColorSpace != VpxColorSpace.Srgb) + { + ColorRange = (VpxColorRange)rb.ReadBit(); + if (Profile == BitstreamProfile.Profile1 || Profile == BitstreamProfile.Profile3) + { + SubsamplingX = rb.ReadBit(); + SubsamplingY = rb.ReadBit(); + if (SubsamplingX == 1 && SubsamplingY == 1) + { + Error.InternalError(CodecErr.UnsupBitstream, + "4:2:0 color not supported in profile 1 or 3"); + } + + if (rb.ReadBit() != 0) + { + Error.InternalError(CodecErr.UnsupBitstream, "Reserved bit set"); + } + } + else + { + SubsamplingY = SubsamplingX = 1; + } + } + else + { + ColorRange = VpxColorRange.Full; + if (Profile == BitstreamProfile.Profile1 || Profile == BitstreamProfile.Profile3) + { + // Note if colorspace is SRGB then 4:4:4 chroma sampling is assumed. + // 4:2:2 or 4:4:0 chroma sampling is not allowed. + SubsamplingY = SubsamplingX = 0; + if (rb.ReadBit() != 0) + { + Error.InternalError(CodecErr.UnsupBitstream, "Reserved bit set"); + } + } + else + { + Error.InternalError(CodecErr.UnsupBitstream, "4:4:4 color not supported in profile 0 or 2"); + } + } + } + + public void AdaptModeProbs() + { + ref Vp9EntropyProbs fc = ref Fc.Value; + ref Vp9EntropyProbs preFc = ref FrameContexts[(int)FrameContextIdx]; + ref Vp9BackwardUpdates counts = ref Counts.Value; + + for (int i = 0; i < Constants.IntraInterContexts; i++) + { + fc.IntraInterProb[i] = Prob.ModeMvMergeProbs(preFc.IntraInterProb[i], ref counts.IntraInter[i]); + } + + for (int i = 0; i < Constants.CompInterContexts; i++) + { + fc.CompInterProb[i] = Prob.ModeMvMergeProbs(preFc.CompInterProb[i], ref counts.CompInter[i]); + } + + for (int i = 0; i < Constants.RefContexts; i++) + { + fc.CompRefProb[i] = Prob.ModeMvMergeProbs(preFc.CompRefProb[i], ref counts.CompRef[i]); + } + + for (int i = 0; i < Constants.RefContexts; i++) + { + for (int j = 0; j < 2; j++) + { + fc.SingleRefProb[i][j] = + Prob.ModeMvMergeProbs(preFc.SingleRefProb[i][j], ref counts.SingleRef[i][j]); + } + } + + for (int i = 0; i < Constants.InterModeContexts; i++) + { + Prob.VpxTreeMergeProbs( + EntropyMode.InterModeTree, + preFc.InterModeProb[i].AsSpan(), + counts.InterMode[i].AsSpan(), + fc.InterModeProb[i].AsSpan()); + } + + for (int i = 0; i < EntropyMode.BlockSizeGroups; i++) + { + Prob.VpxTreeMergeProbs( + EntropyMode.IntraModeTree, + preFc.YModeProb[i].AsSpan(), + counts.YMode[i].AsSpan(), + fc.YModeProb[i].AsSpan()); + } + + for (int i = 0; i < Constants.IntraModes; ++i) + { + Prob.VpxTreeMergeProbs( + EntropyMode.IntraModeTree, + preFc.UvModeProb[i].AsSpan(), + counts.UvMode[i].AsSpan(), + fc.UvModeProb[i].AsSpan()); + } + + for (int i = 0; i < Constants.PartitionContexts; i++) + { + Prob.VpxTreeMergeProbs( + EntropyMode.PartitionTree, + preFc.PartitionProb[i].AsSpan(), + counts.Partition[i].AsSpan(), + fc.PartitionProb[i].AsSpan()); + } + + if (InterpFilter == Constants.Switchable) + { + for (int i = 0; i < Constants.SwitchableFilterContexts; i++) + { + Prob.VpxTreeMergeProbs( + EntropyMode.SwitchableInterpTree, + preFc.SwitchableInterpProb[i].AsSpan(), + counts.SwitchableInterp[i].AsSpan(), + fc.SwitchableInterpProb[i].AsSpan()); + } + } + + if (TxMode == TxMode.TxModeSelect) + { + Array1> branchCt8X8P = new(); + Array2> branchCt16X16P = new(); + Array3> branchCt32X32P = new(); + + for (int i = 0; i < EntropyMode.TxSizeContexts; ++i) + { + EntropyMode.TxCountsToBranchCounts8X8(counts.Tx8x8[i].AsSpan(), ref branchCt8X8P); + for (int j = 0; j < (int)TxSize.TxSizes - 3; ++j) + { + fc.Tx8x8Prob[i][j] = Prob.ModeMvMergeProbs(preFc.Tx8x8Prob[i][j], ref branchCt8X8P[j]); + } + + EntropyMode.TxCountsToBranchCounts16X16(counts.Tx16x16[i].AsSpan(), ref branchCt16X16P); + for (int j = 0; j < (int)TxSize.TxSizes - 2; ++j) + { + fc.Tx16x16Prob[i][j] = + Prob.ModeMvMergeProbs(preFc.Tx16x16Prob[i][j], ref branchCt16X16P[j]); + } + + EntropyMode.TxCountsToBranchCounts32X32(counts.Tx32x32[i].AsSpan(), ref branchCt32X32P); + for (int j = 0; j < (int)TxSize.TxSizes - 1; ++j) + { + fc.Tx32x32Prob[i][j] = + Prob.ModeMvMergeProbs(preFc.Tx32x32Prob[i][j], ref branchCt32X32P[j]); + } + } + } + + for (int i = 0; i < Constants.SkipContexts; ++i) + { + fc.SkipProb[i] = Prob.ModeMvMergeProbs(preFc.SkipProb[i], ref counts.Skip[i]); + } + } + + public void AdaptCoefProbs() + { + byte t; + uint countSat, updateFactor; + + if (FrameIsIntraOnly()) + { + updateFactor = Entropy.CoefMaxUpdateFactorKey; + countSat = Entropy.CoefCountSatKey; + } + else if (LastFrameType == FrameType.KeyFrame) + { + updateFactor = Entropy.CoefMaxUpdateFactorAfterKey; /* adapt quickly */ + countSat = Entropy.CoefCountSatAfterKey; + } + else + { + updateFactor = Entropy.CoefMaxUpdateFactor; + countSat = Entropy.CoefCountSat; + } + + for (t = (int)TxSize.Tx4X4; t <= (int)TxSize.Tx32X32; t++) + { + AdaptCoefProbs(t, countSat, updateFactor); + } + } + + public void SetMvs(ReadOnlySpan mvs) + { + if (mvs.Length > PrevFrameMvs.Length) + { + throw new ArgumentException( + $"Size mismatch, expected: {PrevFrameMvs.Length}, but got: {mvs.Length}."); + } + + for (int i = 0; i < mvs.Length; i++) + { + ref MvRef mv = ref PrevFrameMvs[i]; + + mv.Mv[0].Row = mvs[i].Mvs[0].Row; + mv.Mv[0].Col = mvs[i].Mvs[0].Col; + mv.Mv[1].Row = mvs[i].Mvs[1].Row; + mv.Mv[1].Col = mvs[i].Mvs[1].Col; + + mv.RefFrame[0] = (sbyte)mvs[i].RefFrames[0]; + mv.RefFrame[1] = (sbyte)mvs[i].RefFrames[1]; + } + } + + public void GetMvs(Span mvs) + { + if (mvs.Length > CurFrameMvs.Length) + { + throw new ArgumentException( + $"Size mismatch, expected: {CurFrameMvs.Length}, but got: {mvs.Length}."); + } + + for (int i = 0; i < mvs.Length; i++) + { + ref MvRef mv = ref CurFrameMvs[i]; + + mvs[i].Mvs[0].Row = mv.Mv[0].Row; + mvs[i].Mvs[0].Col = mv.Mv[0].Col; + mvs[i].Mvs[1].Row = mv.Mv[1].Row; + mvs[i].Mvs[1].Col = mv.Mv[1].Col; + + mvs[i].RefFrames[0] = mv.RefFrame[0]; + mvs[i].RefFrames[1] = mv.RefFrame[1]; + } + } + + private void AdaptCoefProbs(byte txSize, uint countSat, uint updateFactor) + { + ref Vp9EntropyProbs preFc = ref FrameContexts[(int)FrameContextIdx]; + ref Array2>>>> probs = ref Fc.Value.CoefProbs[txSize]; + ref Array2>>>> preProbs = ref preFc.CoefProbs[txSize]; + ref Array2>>>> counts = ref Counts.Value.Coef[txSize]; + ref Array2>>> eobCounts = ref Counts.Value.EobBranch[txSize]; + + for (int i = 0; i < Constants.PlaneTypes; ++i) + { + for (int j = 0; j < Entropy.RefTypes; ++j) + { + for (int k = 0; k < Entropy.CoefBands; ++k) + { + for (int l = 0; l < Entropy.BAND_COEFF_CONTEXTS(k); ++l) + { + int n0 = (int)counts[i][j][k][l][Entropy.ZeroToken]; + int n1 = (int)counts[i][j][k][l][Entropy.OneToken]; + int n2 = (int)counts[i][j][k][l][Entropy.TwoToken]; + int neob = (int)counts[i][j][k][l][Entropy.EobModelToken]; + Array3> branchCt = new(); + branchCt[0][0] = (uint)neob; + branchCt[0][1] = (uint)(eobCounts[i][j][k][l] - neob); + branchCt[1][0] = (uint)n0; + branchCt[1][1] = (uint)(n1 + n2); + branchCt[2][0] = (uint)n1; + branchCt[2][1] = (uint)n2; + for (int m = 0; m < Entropy.UnconstrainedNodes; ++m) + { + probs[i][j][k][l][m] = Prob.MergeProbs(preProbs[i][j][k][l][m], ref branchCt[m], + countSat, updateFactor); + } + } + } + } + } + } + + public void DefaultCoefProbs() + { + Entropy.CopyProbs(ref Fc.Value.CoefProbs[(int)TxSize.Tx4X4], Entropy.DefaultCoefProbs4X4); + Entropy.CopyProbs(ref Fc.Value.CoefProbs[(int)TxSize.Tx8X8], Entropy.DefaultCoefProbs8X8); + Entropy.CopyProbs(ref Fc.Value.CoefProbs[(int)TxSize.Tx16X16], Entropy.DefaultCoefProbs16X16); + Entropy.CopyProbs(ref Fc.Value.CoefProbs[(int)TxSize.Tx32X32], Entropy.DefaultCoefProbs32X32); + } } -} +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Vp9Decoder.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Vp9Decoder.cs new file mode 100644 index 000000000..11c121cfe --- /dev/null +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Vp9Decoder.cs @@ -0,0 +1,410 @@ +using Ryujinx.Common.Memory; +using Ryujinx.Graphics.Nvdec.Vp9.Common; +using Ryujinx.Graphics.Video; +using System.Diagnostics; + +namespace Ryujinx.Graphics.Nvdec.Vp9.Types +{ + internal struct Vp9Decoder + { + public Vp9Common Common; + + public int ReadyForNewData; + + public int RefreshFrameFlags; + + public int NeedResync; // Wait for key/intra-only frame. + public int HoldRefBuf; // Hold the reference buffer. + + private static void DecreaseRefCount(int idx, ref Array12 frameBufs, ref BufferPool pool) + { + if (idx >= 0 && frameBufs[idx].RefCount > 0) + { + --frameBufs[idx].RefCount; + // A worker may only get a free framebuffer index when calling GetFreeFb. + // But the private buffer is not set up until finish decoding header. + // So any error happens during decoding header, the frame_bufs will not + // have valid priv buffer. + if (frameBufs[idx].Released == 0 && frameBufs[idx].RefCount == 0 && + !frameBufs[idx].RawFrameBuffer.Priv.IsNull) + { + FrameBuffers.ReleaseFrameBuffer(pool.CbPriv, ref frameBufs[idx].RawFrameBuffer); + frameBufs[idx].Released = 1; + } + } + } + + public void Create(MemoryAllocator allocator, ref BufferPool pool) + { + ref Vp9Common cm = ref Common; + + cm.CheckMemError(ref cm.Fc, + new Ptr(ref allocator.Allocate(1)[0])); + cm.CheckMemError(ref cm.FrameContexts, + allocator.Allocate(Constants.FrameContexts)); + + for (int i = 0; i < EntropyMode.KfYModeProb.Length; i++) + { + for (int j = 0; j < EntropyMode.KfYModeProb[i].Length; j++) + { + for (int k = 0; k < EntropyMode.KfYModeProb[i][j].Length; k++) + { + cm.Fc.Value.KfYModeProb[i][j][k] = EntropyMode.KfYModeProb[i][j][k]; + } + } + } + + for (int i = 0; i < EntropyMode.KfUvModeProb.Length; i++) + { + for (int j = 0; j < EntropyMode.KfUvModeProb[i].Length; j++) + { + cm.Fc.Value.KfUvModeProb[i][j] = EntropyMode.KfUvModeProb[i][j]; + } + } + + byte[][] kfPartitionProbs = + [ + // 8x8 . 4x4 + [158, 97, 94], // a/l both not split + [93, 24, 99], // a split, l not split + [85, 119, 44], // l split, a not split + [62, 59, 67], // a/l both split + + // 16x16 . 8x8 + [149, 53, 53], // a/l both not split + [94, 20, 48], // a split, l not split + [83, 53, 24], // l split, a not split + [52, 18, 18], // a/l both split + + // 32x32 . 16x16 + [150, 40, 39], // a/l both not split + [78, 12, 26], // a split, l not split + [67, 33, 11], // l split, a not split + [24, 7, 5], // a/l both split + + // 64x64 . 32x32 + [174, 35, 49], // a/l both not split + [68, 11, 27], // a split, l not split + [57, 15, 9], // l split, a not split + [12, 3, 3] // a/l both split + ]; + + for (int i = 0; i < kfPartitionProbs.Length; i++) + { + for (int j = 0; j < kfPartitionProbs[i].Length; j++) + { + cm.Fc.Value.KfPartitionProb[i][j] = kfPartitionProbs[i][j]; + } + } + + cm.Counts = new Ptr(ref allocator.Allocate(1)[0]); + + NeedResync = 1; + + // Initialize the references to not point to any frame buffers. + for (int i = 0; i < 8; i++) + { + cm.RefFrameMap[i] = -1; + cm.NextRefFrameMap[i] = -1; + } + + cm.CurrentVideoFrame = 0; + ReadyForNewData = 1; + Common.BufferPool = new Ptr(ref pool); + + cm.BitDepth = BitDepth.Bits8; + cm.DequantBitDepth = BitDepth.Bits8; + + // vp9_loop_filter_init(ref cm); + } + + /* If any buffer updating is signaled it should be done here. */ + private void SwapFrameBuffers() + { + int refIndex = 0, mask; + ref Vp9Common cm = ref Common; + ref BufferPool pool = ref cm.BufferPool.Value; + ref Array12 frameBufs = ref cm.BufferPool.Value.FrameBufs; + + for (mask = RefreshFrameFlags; mask != 0; mask >>= 1) + { + int oldIdx = cm.RefFrameMap[refIndex]; + // Current thread releases the holding of reference frame. + DecreaseRefCount(oldIdx, ref frameBufs, ref pool); + + // Release the reference frame in reference map. + if ((mask & 1) != 0) + { + DecreaseRefCount(oldIdx, ref frameBufs, ref pool); + } + + cm.RefFrameMap[refIndex] = cm.NextRefFrameMap[refIndex]; + ++refIndex; + } + + // Current thread releases the holding of reference frame. + for (; refIndex < Constants.RefFrames && cm.ShowExistingFrame == 0; ++refIndex) + { + int oldIdx = cm.RefFrameMap[refIndex]; + DecreaseRefCount(oldIdx, ref frameBufs, ref pool); + cm.RefFrameMap[refIndex] = cm.NextRefFrameMap[refIndex]; + } + + HoldRefBuf = 0; + cm.FrameToShow = new Ptr(ref cm.GetFrameNewBuffer()); + + --frameBufs[cm.NewFbIdx].RefCount; + + // Invalidate these references until the next frame starts. + for (refIndex = 0; refIndex < 3; refIndex++) + { + cm.FrameRefs[refIndex].Idx = RefBuffer.InvalidIdx; + } + } + + public CodecErr ReceiveCompressedData(MemoryAllocator allocator, ulong size, ref ArrayPtr psource) + { + ref Vp9Common cm = ref Common; + ref BufferPool pool = ref cm.BufferPool.Value; + ref Array12 frameBufs = ref cm.BufferPool.Value.FrameBufs; + ArrayPtr source = psource; + CodecErr retcode = 0; + cm.Error.ErrorCode = CodecErr.Ok; + + if (size == 0) + { + // This is used to signal that we are missing frames. + // We do not know if the missing frame(s) was supposed to update + // any of the reference buffers, but we act conservative and + // mark only the last buffer as corrupted. + + if (cm.FrameRefs[0].Idx > 0) + { + cm.FrameRefs[0].Buf.Corrupted = 1; + } + } + + ReadyForNewData = 0; + + // Check if the previous frame was a frame without any references to it. + if (cm.NewFbIdx >= 0 && frameBufs[cm.NewFbIdx].RefCount == 0 && + frameBufs[cm.NewFbIdx].Released == 0) + { + FrameBuffers.ReleaseFrameBuffer(pool.CbPriv, ref frameBufs[cm.NewFbIdx].RawFrameBuffer); + frameBufs[cm.NewFbIdx].Released = 1; + } + + // Find a free frame buffer. Return error if can not find any. + cm.NewFbIdx = cm.GetFreeFb(); + if (cm.NewFbIdx == RefBuffer.InvalidIdx) + { + ReadyForNewData = 1; + cm.Error.InternalError(CodecErr.MemError, "Unable to find free frame buffer"); + + return cm.Error.ErrorCode; + } + + // Assign a MV array to the frame buffer. + cm.CurFrame = new Ptr(ref pool.FrameBufs[cm.NewFbIdx]); + + HoldRefBuf = 0; + + DecodeFrame.Decode(allocator, ref this, new ArrayPtr(ref source[0], (int)size), out psource); + + SwapFrameBuffers(); + + // vpx_clear_system_state(); + + if (cm.ShowExistingFrame == 0) + { + cm.LastShowFrame = cm.ShowFrame; + cm.PrevFrame = cm.CurFrame; + + if (cm.PrevFrameMvs.IsNull || cm.PrevFrameMvs.Length != cm.CurFrameMvs.Length) + { + allocator.Free(cm.PrevFrameMvs); + cm.PrevFrameMvs = allocator.Allocate(cm.CurFrameMvs.Length); + } + + cm.CurFrameMvs.AsSpan().CopyTo(cm.PrevFrameMvs.AsSpan()); + if (cm.Seg.Enabled) + { + cm.SwapCurrentAndLastSegMap(); + } + } + + if (cm.ShowFrame != 0) + { + cm.CurShowFrameFbIdx = cm.NewFbIdx; + } + + // Update progress in frame parallel decode. + cm.LastWidth = cm.Width; + cm.LastHeight = cm.Height; + if (cm.ShowFrame != 0) + { + cm.CurrentVideoFrame++; + } + + return retcode; + } + + public int GetRawFrame(ref Surface sd) + { + ref Vp9Common cm = ref Common; + int ret = -1; + + if (ReadyForNewData == 1) + { + return ret; + } + + ReadyForNewData = 1; + + if (cm.ShowFrame == 0) + { + return ret; + } + + ReadyForNewData = 1; + + sd = cm.FrameToShow.Value; + ret = 0; + + return ret; + } + + public CodecErr Decode(MemoryAllocator allocator, ArrayPtr data) + { + ArrayPtr dataStart = data; + CodecErr res; + Array8 frameSizes = new(); + int frameCount = 0; + + res = Decoder.ParseSuperframeIndex(data, (ulong)data.Length, ref frameSizes, out frameCount); + if (res != CodecErr.Ok) + { + return res; + } + + // Decode in serial mode. + if (frameCount > 0) + { + for (int i = 0; i < frameCount; ++i) + { + ArrayPtr dataStartCopy = dataStart; + uint frameSize = frameSizes[i]; + if (frameSize > (uint)dataStart.Length) + { + return CodecErr.CorruptFrame; + } + + res = ReceiveCompressedData(allocator, frameSize, ref dataStartCopy); + if (res != CodecErr.Ok) + { + return res; + } + + dataStart = dataStart.Slice((int)frameSize); + } + } + else + { + while (dataStart.Length != 0) + { + uint frameSize = (uint)dataStart.Length; + res = ReceiveCompressedData(allocator, frameSize, ref dataStart); + if (res != CodecErr.Ok) + { + return res; + } + + // Account for suboptimal termination by the encoder. + while (dataStart.Length != 0) + { + byte marker = Decoder.ReadMarker(dataStart); + if (marker != 0) + { + break; + } + + dataStart = dataStart.Slice(1); + } + } + } + + return res; + } + } + + internal static class Decoder + { + public static byte ReadMarker(ArrayPtr data) + { + return data[0]; + } + + public static CodecErr ParseSuperframeIndex(ArrayPtr data, ulong dataSz, ref Array8 sizes, out int count) + { + // A chunk ending with a byte matching 0xc0 is an invalid chunk unless + // it is a super frame index. If the last byte of real video compression + // data is 0xc0 the encoder must add a 0 byte. If we have the marker but + // not the associated matching marker byte at the front of the index we have + // an invalid bitstream and need to return an error. + + byte marker; + + Debug.Assert(dataSz != 0); + marker = ReadMarker(data.Slice((int)dataSz - 1)); + count = 0; + + if ((marker & 0xe0) == 0xc0) + { + uint frames = (uint)(marker & 0x7) + 1; + uint mag = (uint)((marker >> 3) & 0x3) + 1; + ulong indexSz = 2 + (mag * frames); + + // This chunk is marked as having a superframe index but doesn't have + // enough data for it, thus it's an invalid superframe index. + if (dataSz < indexSz) + { + return CodecErr.CorruptFrame; + } + + { + byte marker2 = ReadMarker(data.Slice((int)(dataSz - indexSz))); + + // This chunk is marked as having a superframe index but doesn't have + // the matching marker byte at the front of the index therefore it's an + // invalid chunk. + if (marker != marker2) + { + return CodecErr.CorruptFrame; + } + } + + { + // Found a valid superframe index. + ArrayPtr x = data.Slice((int)(dataSz - indexSz + 1)); + + for (int i = 0; i < frames; ++i) + { + uint thisSz = 0; + + for (int j = 0; j < mag; ++j) + { + thisSz |= (uint)x[0] << j * 8; + x = x.Slice(1); + } + + sizes[i] = thisSz; + } + + count = (int)frames; + } + } + + return CodecErr.Ok; + } + } +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/VpxCodecFrameBuffer.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/VpxCodecFrameBuffer.cs new file mode 100644 index 000000000..358de79dd --- /dev/null +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/VpxCodecFrameBuffer.cs @@ -0,0 +1,10 @@ +using Ryujinx.Common.Memory; + +namespace Ryujinx.Graphics.Nvdec.Vp9.Types +{ + internal struct VpxCodecFrameBuffer + { + public ArrayPtr Data; + public Ptr Priv; + } +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/VpxColorRange.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/VpxColorRange.cs new file mode 100644 index 000000000..9f8c7c53d --- /dev/null +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/VpxColorRange.cs @@ -0,0 +1,11 @@ +namespace Ryujinx.Graphics.Nvdec.Vp9.Types +{ + internal enum VpxColorRange + { + // Y [16..235], UV [16..240] + Studio, + + // YUV/RGB [0..255] + Full + } +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/VpxColorSpace.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/VpxColorSpace.cs new file mode 100644 index 000000000..a1706c0d0 --- /dev/null +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/VpxColorSpace.cs @@ -0,0 +1,29 @@ +namespace Ryujinx.Graphics.Nvdec.Vp9.Types +{ + internal enum VpxColorSpace + { + // Unknown + Unknown, + + // BT.601 + Bt601, + + // BT.709 + Bt709, + + // SMPTE.170 + Smpte170, + + // SMPTE.240 + Smpte240, + + // BT.2020 + Bt2020, + + // Reserved + Reserved, + + // sRGB + Srgb + } +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Nvdec/Types/Vp9/PictureInfo.cs b/src/Ryujinx.Graphics.Nvdec/Types/Vp9/PictureInfo.cs index 24c18b94c..9da64bfab 100644 --- a/src/Ryujinx.Graphics.Nvdec/Types/Vp9/PictureInfo.cs +++ b/src/Ryujinx.Graphics.Nvdec/Types/Vp9/PictureInfo.cs @@ -59,6 +59,8 @@ namespace Ryujinx.Graphics.Nvdec.Types.Vp9 Flags.HasFlag(FrameFlags.LastShowFrame) && !Flags.HasFlag(FrameFlags.LastFrameIsKeyFrame), RefFrameSignBias = RefFrameSignBias, + LoopFilterLevel = FirstLevel, + LoopFilterSharpnessLevel = SharpnessLevel, BaseQIndex = BaseQIndex, YDcDeltaQ = YDcDeltaQ, UvDcDeltaQ = UvDcDeltaQ, 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.Graphics.Video/Vp9PictureInfo.cs b/src/Ryujinx.Graphics.Video/Vp9PictureInfo.cs index bcdaf0dfc..09f6e6664 100644 --- a/src/Ryujinx.Graphics.Video/Vp9PictureInfo.cs +++ b/src/Ryujinx.Graphics.Video/Vp9PictureInfo.cs @@ -10,6 +10,8 @@ namespace Ryujinx.Graphics.Video public bool IsKeyFrame; public bool IntraOnly; public Array4 RefFrameSignBias; + public int LoopFilterLevel; + public int LoopFilterSharpnessLevel; public int BaseQIndex; public int YDcDeltaQ; public int UvDcDeltaQ; @@ -36,4 +38,4 @@ namespace Ryujinx.Graphics.Video public Vp9EntropyProbs Entropy; public Vp9BackwardUpdates BackwardUpdateCounts; } -} +} \ No newline at end of file diff --git a/src/Ryujinx.HLE/HOS/Applets/Browser/BrowserApplet.cs b/src/Ryujinx.HLE/HOS/Applets/Browser/BrowserApplet.cs index 33d00cf34..488eca291 100644 --- a/src/Ryujinx.HLE/HOS/Applets/Browser/BrowserApplet.cs +++ b/src/Ryujinx.HLE/HOS/Applets/Browser/BrowserApplet.cs @@ -41,7 +41,7 @@ namespace Ryujinx.HLE.HOS.Applets.Browser { List result = [ - new BrowserOutput(BrowserOutputType.ExitReason, (uint)WebExitReason.ExitButton) + new(BrowserOutputType.ExitReason, (uint)WebExitReason.ExitButton) ]; _normalSession.Push(BuildResponseNew(result)); 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/Ldn/UserServiceCreator/LdnRyu/Proxy/P2pProxyServer.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Proxy/P2pProxyServer.cs index 4a217b88b..5e2f59d18 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Proxy/P2pProxyServer.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Proxy/P2pProxyServer.cs @@ -113,7 +113,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy public async Task NatPunch() { NatDiscoverer discoverer = new(); - CancellationTokenSource cts = new(5000); + CancellationTokenSource cts = new(2500); NatDevice device; diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs index 8475424ce..0a592d542 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs @@ -34,6 +34,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd { if (errorCode != LinuxError.SUCCESS) { + if (errorCode != LinuxError.EWOULDBLOCK) + { + Logger.Warning?.Print(LogClass.ServiceBsd, $"Operation failed with error {errorCode}."); + } result = -1; } @@ -66,6 +70,8 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd BsdSocketType type = (BsdSocketType)context.RequestData.ReadInt32(); ProtocolType protocol = (ProtocolType)context.RequestData.ReadInt32(); + Logger.Info?.PrintMsg(LogClass.ServiceBsd, $"Creating socket with domain={domain}, type={type}, protocol={protocol}"); + BsdSocketCreationFlags creationFlags = (BsdSocketCreationFlags)((int)type >> (int)BsdSocketCreationFlags.FlagsShift); type &= BsdSocketType.TypeMask; @@ -95,12 +101,21 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd } } - ISocket newBsdSocket = new ManagedSocket(netDomain, (SocketType)type, protocol, context.Device.Configuration.MultiplayerLanInterfaceId) - { - Blocking = !creationFlags.HasFlag(BsdSocketCreationFlags.NonBlocking), - }; - LinuxError errno = LinuxError.SUCCESS; + ISocket newBsdSocket; + + try + { + newBsdSocket = new ManagedSocket(netDomain, (SocketType)type, protocol, context.Device.Configuration.MultiplayerLanInterfaceId) + { + Blocking = !creationFlags.HasFlag(BsdSocketCreationFlags.NonBlocking), + }; + } + catch (SocketException exception) + { + LinuxError errNo = WinSockHelper.ConvertError((WsaError)exception.ErrorCode); + return WriteBsdResult(context, 0, errNo); + } int newSockFd = _context.RegisterFileDescriptor(newBsdSocket); @@ -111,6 +126,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd if (exempt) { + Logger.Info?.Print(LogClass.ServiceBsd, "Disconnecting exempt socket."); newBsdSocket.Disconnect(); } @@ -797,7 +813,11 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd { errno = socket.Listen(backlog); } - + else + { + Logger.Warning?.PrintMsg(LogClass.ServiceBsd, $"Invalid socket fd '{socketFd}'."); + } + return WriteBsdResult(context, 0, errno); } diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/ManagedSocket.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/ManagedSocket.cs index 981fe0a8f..3c367660a 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/ManagedSocket.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/ManagedSocket.cs @@ -92,18 +92,30 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl { newSocket = new ManagedSocket(Socket.Accept()); + IPEndPoint remoteEndPoint = newSocket.RemoteEndPoint; + bool isPrivateIp = remoteEndPoint.Address.ToString().StartsWith("192.168."); + Logger.Info?.PrintMsg(LogClass.ServiceBsd, + isPrivateIp + ? $"Accepted connection from {ProtocolType}/{remoteEndPoint.Address}:{remoteEndPoint.Port}" + : $"Accepted connection from {ProtocolType}/***:{remoteEndPoint.Port}"); + return LinuxError.SUCCESS; } catch (SocketException exception) { newSocket = null; + if (exception.SocketErrorCode != SocketError.WouldBlock) + { + Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}"); + } return WinSockHelper.ConvertError((WsaError)exception.ErrorCode); } } public LinuxError Bind(IPEndPoint localEndPoint) { + Logger.Info?.PrintMsg(LogClass.ServiceBsd, $"Socket binding to: {ProtocolType}/{localEndPoint.Port}"); try { Socket.Bind(localEndPoint); @@ -112,6 +124,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl } catch (SocketException exception) { + if (exception.SocketErrorCode != SocketError.WouldBlock) + { + Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}"); + } return WinSockHelper.ConvertError((WsaError)exception.ErrorCode); } } @@ -123,6 +139,15 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl public LinuxError Connect(IPEndPoint remoteEndPoint) { + bool isLDNPrivateIP = remoteEndPoint.Address.ToString().StartsWith("192.168."); + if (isLDNPrivateIP) + { + Logger.Info?.PrintMsg(LogClass.ServiceBsd, $"Connecting to: {ProtocolType}/{remoteEndPoint.Address}:{remoteEndPoint.Port}"); + } + else + { + Logger.Info?.PrintMsg(LogClass.ServiceBsd, $"Connecting to: {ProtocolType}/***:{remoteEndPoint.Port}"); + } try { Socket.Connect(remoteEndPoint); @@ -137,6 +162,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl } else { + if (exception.SocketErrorCode != SocketError.WouldBlock) + { + Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}"); + } return WinSockHelper.ConvertError((WsaError)exception.ErrorCode); } } @@ -144,11 +173,13 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl public void Disconnect() { + Logger.Info?.Print(LogClass.ServiceBsd, "Socket disconnecting"); Socket.Disconnect(true); } public void Dispose() { + Logger.Info?.Print(LogClass.ServiceBsd, "Socket closed"); Socket.Close(); Socket.Dispose(); } @@ -159,10 +190,16 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl { Socket.Listen(backlog); + Logger.Info?.PrintMsg(LogClass.ServiceBsd, $"Socket listening: {ProtocolType}/{(Socket.LocalEndPoint as IPEndPoint).Port}"); + return LinuxError.SUCCESS; } catch (SocketException exception) { + if (exception.SocketErrorCode != SocketError.WouldBlock) + { + Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}"); + } return WinSockHelper.ConvertError((WsaError)exception.ErrorCode); } } @@ -182,11 +219,15 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl } catch (SocketException exception) { + if (exception.SocketErrorCode != SocketError.WouldBlock) + { + Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}"); + } return WinSockHelper.ConvertError((WsaError)exception.ErrorCode); } } - bool hasEmittedBlockingWarning = false; + private bool _hasEmittedBlockingWarning; public LinuxError Receive(out int receiveSize, Span buffer, BsdSocketFlags flags) { @@ -202,10 +243,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl shouldBlockAfterOperation = true; } - if (Blocking && !hasEmittedBlockingWarning) + if (Blocking && !_hasEmittedBlockingWarning) { Logger.Warning?.PrintMsg(LogClass.ServiceBsd, "Blocking socket operations are not yet working properly. Expect network errors."); - hasEmittedBlockingWarning = true; + _hasEmittedBlockingWarning = true; } receiveSize = Socket.Receive(buffer, ConvertBsdSocketFlags(flags)); @@ -214,6 +255,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl } catch (SocketException exception) { + if (exception.SocketErrorCode != SocketError.WouldBlock) + { + Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}"); + } receiveSize = -1; result = WinSockHelper.ConvertError((WsaError)exception.ErrorCode); @@ -245,10 +290,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl shouldBlockAfterOperation = true; } - if (Blocking && !hasEmittedBlockingWarning) + if (Blocking && !_hasEmittedBlockingWarning) { Logger.Warning?.PrintMsg(LogClass.ServiceBsd, "Blocking socket operations are not yet working properly. Expect network errors."); - hasEmittedBlockingWarning = true; + _hasEmittedBlockingWarning = true; } if (!Socket.IsBound) @@ -265,6 +310,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl } catch (SocketException exception) { + if (exception.SocketErrorCode != SocketError.WouldBlock) + { + Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}"); + } receiveSize = -1; result = WinSockHelper.ConvertError((WsaError)exception.ErrorCode); @@ -288,6 +337,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl } catch (SocketException exception) { + if (exception.SocketErrorCode != SocketError.WouldBlock) + { + Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}"); + } sendSize = -1; return WinSockHelper.ConvertError((WsaError)exception.ErrorCode); @@ -304,6 +357,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl } catch (SocketException exception) { + if (exception.SocketErrorCode != SocketError.WouldBlock) + { + Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}"); + } sendSize = -1; return WinSockHelper.ConvertError((WsaError)exception.ErrorCode); @@ -341,6 +398,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl } catch (SocketException exception) { + if (exception.SocketErrorCode != SocketError.WouldBlock) + { + Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}"); + } return WinSockHelper.ConvertError((WsaError)exception.ErrorCode); } } @@ -387,6 +448,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl } catch (SocketException exception) { + if (exception.SocketErrorCode != SocketError.WouldBlock) + { + Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}"); + } return WinSockHelper.ConvertError((WsaError)exception.ErrorCode); } } @@ -519,6 +584,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl } catch (SocketException exception) { + if (exception.SocketErrorCode != SocketError.WouldBlock) + { + Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}"); + } return WinSockHelper.ConvertError((WsaError)exception.ErrorCode); } } @@ -557,6 +626,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl } catch (SocketException exception) { + if (exception.SocketErrorCode != SocketError.WouldBlock) + { + Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}"); + } return WinSockHelper.ConvertError((WsaError)exception.ErrorCode); } } 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 9f206176d..b442cf802 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Proxy/SocketHelpers.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Proxy/SocketHelpers.cs @@ -1,4 +1,6 @@ +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; @@ -64,10 +66,18 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Proxy { if (_proxy.Supported(domain, type, protocol)) { + Logger.Info?.PrintMsg(LogClass.ServiceBsd, $"Socket is using LDN proxy"); return new LdnProxySocket(domain, type, protocol, _proxy); } + else + { + Logger.Warning?.PrintMsg(LogClass.ServiceBsd, $"LDN proxy does not support socket {domain}, {type}, {protocol}"); + } + } + else + { + Logger.Info?.PrintMsg(LogClass.ServiceBsd, $"Opening socket using host networking stack"); } - return new DefaultSocket(domain, type, protocol, lanInterfaceId); } } diff --git a/src/Ryujinx.HLE/HLEConfiguration.cs b/src/Ryujinx.HLE/HleConfiguration.cs similarity index 88% rename from src/Ryujinx.HLE/HLEConfiguration.cs rename to src/Ryujinx.HLE/HleConfiguration.cs index 0b7ae3974..97835033e 100644 --- a/src/Ryujinx.HLE/HLEConfiguration.cs +++ b/src/Ryujinx.HLE/HleConfiguration.cs @@ -15,55 +15,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. @@ -195,15 +195,7 @@ namespace Ryujinx.HLE /// This cannot be changed after instantiation. public EnabledDirtyHack[] Hacks { internal get; set; } - public HLEConfiguration(VirtualFileSystem virtualFileSystem, - LibHacHorizonManager libHacHorizonManager, - ContentManager contentManager, - AccountManager accountManager, - UserChannelPersistence userChannelPersistence, - IRenderer gpuRenderer, - IHardwareDeviceDriver audioDeviceDriver, - MemoryConfiguration memoryConfiguration, - IHostUIHandler hostUIHandler, + public HleConfiguration(MemoryConfiguration memoryConfiguration, SystemLanguage systemLanguage, RegionCode region, VSyncMode vSyncMode, @@ -227,15 +219,7 @@ namespace Ryujinx.HLE int customVSyncInterval, EnabledDirtyHack[] dirtyHacks = null) { - VirtualFileSystem = virtualFileSystem; - LibHacHorizonManager = libHacHorizonManager; - AccountManager = accountManager; - ContentManager = contentManager; - UserChannelPersistence = userChannelPersistence; - GpuRenderer = gpuRenderer; - AudioDeviceDriver = audioDeviceDriver; MemoryConfiguration = memoryConfiguration; - HostUIHandler = hostUIHandler; SystemLanguage = systemLanguage; Region = region; VSyncMode = vSyncMode; @@ -259,5 +243,30 @@ 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 + ) + { + VirtualFileSystem = virtualFileSystem; + LibHacHorizonManager = libHacHorizonManager; + AccountManager = accountManager; + ContentManager = contentManager; + UserChannelPersistence = userChannelPersistence; + GpuRenderer = gpuRenderer; + AudioDeviceDriver = audioDeviceDriver; + HostUIHandler = hostUIHandler; + 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 86b04061e..df5b48103 100644 --- a/src/Ryujinx.HLE/Switch.cs +++ b/src/Ryujinx.HLE/Switch.cs @@ -20,7 +20,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; } @@ -44,7 +44,7 @@ namespace Ryujinx.HLE public DirtyHacks DirtyHacks { get; } - public Switch(HLEConfiguration configuration) + public Switch(HleConfiguration configuration) { ArgumentNullException.ThrowIfNull(configuration.GpuRenderer); ArgumentNullException.ThrowIfNull(configuration.AudioDeviceDriver); @@ -94,16 +94,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 3b243eed0..6f1b79d38 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/AppHost.cs b/src/Ryujinx/AppHost.cs index b26921e6a..40c54b6f7 100644 --- a/src/Ryujinx/AppHost.cs +++ b/src/Ryujinx/AppHost.cs @@ -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; @@ -892,14 +891,10 @@ 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, @@ -907,53 +902,19 @@ namespace Ryujinx.Ava _ => 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; - - Device = new Switch(new HLEConfiguration( - VirtualFileSystem, - _viewModel.LibHacHorizonManager, - ContentManager, - _accountManager, - _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 + ) + ); } private static IHardwareDeviceDriver InitializeAudio() @@ -1118,6 +1079,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() @@ -1126,7 +1094,6 @@ namespace Ryujinx.Ava { GraphicsBackend.Vulkan => "Vulkan", GraphicsBackend.OpenGl => "OpenGL", - GraphicsBackend.Metal => "Metal", _ => throw new NotImplementedException() }; @@ -1181,6 +1148,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) @@ -1254,12 +1224,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/Assets/Styles/Styles.xaml b/src/Ryujinx/Assets/Styles/Styles.xaml index 5523f551a..eb40e853c 100644 --- a/src/Ryujinx/Assets/Styles/Styles.xaml +++ b/src/Ryujinx/Assets/Styles/Styles.xaml @@ -1,7 +1,8 @@ - + Content="Add" + Classes="red"/> + + + + + + + + + + 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 684051368..d69c3ee9b 100644 --- a/src/Ryujinx/Assets/locales.json +++ b/src/Ryujinx/Assets/locales.json @@ -87,7 +87,7 @@ "ko_KR": "Mii 편집 애플릿", "no_NO": "Mii-redigeringsapplet", "pl_PL": "", - "pt_BR": "Applet Editor de Mii", + "pt_BR": "Editor de Mii", "ru_RU": "Апплет Mii Editor", "sv_SE": "Redigera Mii-applet", "th_TH": "", @@ -112,7 +112,7 @@ "ko_KR": "독립 실행형 모드로 Mii 편집기 애플릿 열기", "no_NO": "Åpne Mii Redigerings program i eget vindu", "pl_PL": "Otwórz aplet Mii Editor w trybie indywidualnym", - "pt_BR": "Abrir editor Mii em modo avulso", + "pt_BR": "Abrir Editor de Mii em Modo Independente", "ru_RU": "Открывает апплет Mii Editor в автономном режиме", "sv_SE": "Öppna Mii Editor Applet i fristående läge", "th_TH": "เปิดโปรแกรม Mii Editor Applet", @@ -137,12 +137,12 @@ "ko_KR": "마우스 직접 접근", "no_NO": "Direkte tilgang med Mus", "pl_PL": "Bezpośredni dostęp do myszy", - "pt_BR": "Acesso direto ao mouse", + "pt_BR": "Acesso Direto ao Mouse", "ru_RU": "Прямой ввод мыши", "sv_SE": "Direkt musåtkomst", "th_TH": "เข้าถึงเมาส์ได้โดยตรง", "tr_TR": "Doğrudan Mouse Erişimi", - "uk_UA": "Прямий доступ мишею", + "uk_UA": "Пряме керування мишею", "zh_CN": "直通鼠标操作", "zh_TW": "滑鼠直接存取" } @@ -162,7 +162,7 @@ "ko_KR": "메모리 관리자 모드 :", "no_NO": "Memory Manager-modus", "pl_PL": "Tryb menedżera pamięci:", - "pt_BR": "Modo de gerenciamento de memória:", + "pt_BR": "Modo de Gerenciamento da Memória:", "ru_RU": "Режим менеджера памяти:", "sv_SE": "Läge för minnehanterare:", "th_TH": "โหมดจัดการหน่วยความจำ:", @@ -237,7 +237,7 @@ "ko_KR": "호스트 확인 안함(가장 빠르나 위험)", "no_NO": "Vert Ukontrollert (raskets, utrygt)", "pl_PL": "Gospodarza (NIESPRAWDZONY, najszybszy, niebezpieczne)", - "pt_BR": "Hóspede sem verificação (mais rápido, inseguro)", + "pt_BR": "Hóspede sem Verificação (mais rápido, inseguro)", "ru_RU": "Хост не установлен (самый быстрый, небезопасный)", "sv_SE": "Värd inte kontrollerad (snabbaste, osäkert)", "th_TH": "ไม่ได้ตรวจสอบโฮสต์ (เร็วที่สุด, แต่ไม่ปลอดภัย)", @@ -312,7 +312,7 @@ "ko_KR": "파일에서 앱 불러오기(_L)", "no_NO": "_Last inn program fra fil", "pl_PL": "_Załaduj aplikację z pliku", - "pt_BR": "_Abrir ROM do jogo...", + "pt_BR": "_Abrir ROM do Jogo", "ru_RU": "_Добавить приложение из файла", "sv_SE": "_Läs in applikation från fil", "th_TH": "โหลดแอปพลิเคชั่นจากไฟล์", @@ -362,7 +362,7 @@ "ko_KR": "압축 푼 게임 불러오기(_U)", "no_NO": "Last inn _Upakket spill", "pl_PL": "Załaduj _rozpakowaną grę", - "pt_BR": "Abrir jogo _extraído...", + "pt_BR": "Abrir Jogo _Extraído", "ru_RU": "Добавить _распакованную игру", "sv_SE": "Läs in _uppackat spel", "th_TH": "โหลดเกมที่แตกไฟล์แล้ว", @@ -417,7 +417,7 @@ "sv_SE": "Läs in titeluppdateringar från mapp", "th_TH": "โหลดไฟล์อัพเดตจากโฟลเดอร์", "tr_TR": "", - "uk_UA": "Завантажити оновлення заголовків з теки", + "uk_UA": "Завантажити оновлення ігор з теки", "zh_CN": "从文件夹加载游戏更新", "zh_TW": "從資料夾中載入遊戲更新" } @@ -437,7 +437,7 @@ "ko_KR": "Ryujinx 폴더 열기", "no_NO": "Åpne Ryujinx mappe", "pl_PL": "Otwórz folder Ryujinx", - "pt_BR": "Abrir diretório do e_mulador...", + "pt_BR": "Abrir Pasta do Ryujinx", "ru_RU": "Открыть папку Ryujinx", "sv_SE": "Öppna Ryujinx-mapp", "th_TH": "เปิดโฟลเดอร์ Ryujinx", @@ -447,6 +447,31 @@ "zh_TW": "開啟 Ryujinx 資料夾" } }, + { + "ID": "MenuBarFileOpenScreenshotsFolder", + "Translations": { + "ar_SA": "", + "de_DE": "", + "el_GR": "", + "en_US": "Open Screenshots Folder", + "es_ES": "", + "fr_FR": "", + "he_IL": "", + "it_IT": "", + "ja_JP": "", + "ko_KR": "", + "no_NO": "", + "pl_PL": "", + "pt_BR": "Abrir Pasta de Capturas de Tela", + "ru_RU": "Открыть папку со скриншотами", + "sv_SE": "Öppna skärmbildsmappen", + "th_TH": "", + "tr_TR": "", + "uk_UA": "Відкрити теку скріншотів", + "zh_CN": "打开截图文件夹", + "zh_TW": "" + } + }, { "ID": "MenuBarFileOpenLogsFolder", "Translations": { @@ -462,7 +487,7 @@ "ko_KR": "로그 폴더 열기", "no_NO": "Åpne Logg mappen", "pl_PL": "Otwórz folder plików dziennika zdarzeń", - "pt_BR": "Abrir diretório de _logs...", + "pt_BR": "Abrir Pasta de _Logs", "ru_RU": "Открыть папку с логами", "sv_SE": "Öppna loggmapp", "th_TH": "เปิดโฟลเดอร์ Logs", @@ -537,7 +562,7 @@ "ko_KR": "전체 화면 전환", "no_NO": "Fullskjermsvisning av/på", "pl_PL": "Przełącz na tryb pełnoekranowy", - "pt_BR": "_Mudar para tela cheia", + "pt_BR": "_Mudar para Tela Cheia", "ru_RU": "Включить полноэкранный режим", "sv_SE": "Växla helskärm", "th_TH": "สลับเป็นโหมดเต็มหน้าจอ", @@ -562,7 +587,7 @@ "ko_KR": "전체 화면 모드로 게임 시작", "no_NO": "Start spill i Fullskjermmodus", "pl_PL": "Uruchamiaj gry w trybie pełnoekranowym", - "pt_BR": "Iniciar jogos em tela cheia", + "pt_BR": "Iniciar Jogos em Tela Cheia", "ru_RU": "Запускать игры в полноэкранном режиме", "sv_SE": "Starta spel i helskärmsläge", "th_TH": "เริ่มเกมในโหมดเต็มหน้าจอ", @@ -587,12 +612,12 @@ "ko_KR": "UI를 숨긴 상태에서 게임 시작", "no_NO": "Start Spillet med UI Gjemt", "pl_PL": "", - "pt_BR": "Iniciar jogos ocultando a interface", - "ru_RU": "", + "pt_BR": "Iniciar Jogos Ocultando a Interface", + "ru_RU": "Запускать игры скрывая интерфейс", "sv_SE": "Starta spel med dolt användargränssnitt", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Запускати ігри з прихованим інтерфейсом", "zh_CN": "启动游戏时隐藏 UI", "zh_TW": "" } @@ -612,7 +637,7 @@ "ko_KR": "에뮬레이션 중지", "no_NO": "Stopp Emulering", "pl_PL": "Zatrzymaj emulację", - "pt_BR": "_Encerrar emulação", + "pt_BR": "_Parar a Emulação", "ru_RU": "Остановить эмуляцию", "sv_SE": "Stoppa emulering", "th_TH": "หยุดการจำลอง", @@ -662,8 +687,8 @@ "ko_KR": "사용자 프로필 관리(_M)", "no_NO": "_Administrere Brukerprofiler", "pl_PL": "_Zarządzaj profilami użytkowników", - "pt_BR": "_Gerenciar perfis de usuário", - "ru_RU": "_Менеджер учетных записей", + "pt_BR": "_Gerenciar Perfis de Usuário", + "ru_RU": "_Менеджер учётных записей", "sv_SE": "_Hantera användarprofiler", "th_TH": "_จัดการโปรไฟล์ผู้ใช้งาน", "tr_TR": "_Kullanıcı Profillerini Yönet", @@ -712,7 +737,7 @@ "ko_KR": "웨이크업 메시지 시뮬레이션", "no_NO": "Simuler oppvåknings-melding", "pl_PL": "Symuluj wiadomość wybudzania", - "pt_BR": "_Simular mensagem de acordar console", + "pt_BR": "_Simular Mensagem de Acordar o Console", "ru_RU": "Имитировать сообщение пробуждения", "sv_SE": "Simulera uppvakningsmeddelande", "th_TH": "จำลองข้อความปลุก", @@ -787,7 +812,7 @@ "ko_KR": "펌웨어 설치", "no_NO": "Installer fastvare", "pl_PL": "Zainstaluj oprogramowanie", - "pt_BR": "_Instalar firmware", + "pt_BR": "_Instalar Firmware", "ru_RU": "Установка прошивки", "sv_SE": "Installera firmware", "th_TH": "ติดตั้งเฟิร์มแวร์", @@ -937,7 +962,7 @@ "ko_KR": "파일 형식 관리", "no_NO": "Behandle filtyper", "pl_PL": "Zarządzaj rodzajami plików", - "pt_BR": "Gerenciar tipos de arquivo", + "pt_BR": "Gerenciar Tipos de Arquivos", "ru_RU": "Управление типами файлов", "sv_SE": "Hantera filtyper", "th_TH": "จัดการประเภทไฟล์", @@ -962,7 +987,7 @@ "ko_KR": "파일 형식 설치", "no_NO": "Installer filtyper", "pl_PL": "Typy plików instalacyjnych", - "pt_BR": "Instalar tipos de arquivo", + "pt_BR": "Instalar tipos de arquivos", "ru_RU": "Установить типы файлов", "sv_SE": "Installera filtyper", "th_TH": "ติดตั้งประเภทไฟล์", @@ -1012,7 +1037,7 @@ "ko_KR": "XCI 파일 트리머", "no_NO": "Trim XCI-filer", "pl_PL": "", - "pt_BR": "Reduzir arquivos XCI", + "pt_BR": "Reduzir Arquivos XCI", "ru_RU": "Уменьшить размер XCI файлов", "sv_SE": "Optimera XCI-filer", "th_TH": "", @@ -1062,7 +1087,7 @@ "ko_KR": "윈도 창", "no_NO": "Vindu størrelse", "pl_PL": "", - "pt_BR": "Tamanho da janela", + "pt_BR": "Tamanho da Janela", "ru_RU": "Размер окна", "sv_SE": "Fönsterstorlek", "th_TH": "ขนาดหน้าต่าง", @@ -1192,7 +1217,7 @@ "sv_SE": "_Hjälp", "th_TH": "_ช่วยเหลือ", "tr_TR": "_Yardım", - "uk_UA": "_Допомога", + "uk_UA": "_Довідка", "zh_CN": "帮助(_H)", "zh_TW": "說明(_H)" } @@ -1212,7 +1237,7 @@ "ko_KR": "업데이트 확인", "no_NO": "Se etter oppdateringer", "pl_PL": "Sprawdź aktualizacje", - "pt_BR": "_Verificar se há atualizações", + "pt_BR": "_Verificar Atualizações", "ru_RU": "Проверить наличие обновлений", "sv_SE": "Leta efter uppdateringar", "th_TH": "ตรวจสอบอัปเดต", @@ -1262,7 +1287,7 @@ "ko_KR": "자주 묻는 질문(FAQ) 및 문제해결 페이지", "no_NO": "FAQ- og feilsøkingsside", "pl_PL": "", - "pt_BR": "FAQ e solução de problemas", + "pt_BR": "FAQ e Solução de Problemas", "ru_RU": "FAQ & Устранение неполадок", "sv_SE": "Frågor, svar och felsökningssida", "th_TH": "", @@ -1312,7 +1337,7 @@ "ko_KR": "설치 및 구성 안내", "no_NO": "Oppsett- og konfigurasjonsveiledning", "pl_PL": "", - "pt_BR": "Guia de instalação e configuração", + "pt_BR": "Guia de Instalação e Configuração", "ru_RU": "Руководство по установке и настройке", "sv_SE": "Konfigurationsguide", "th_TH": "", @@ -1362,7 +1387,7 @@ "ko_KR": "멀티플레이어(LDN/LAN) 안내", "no_NO": "Flerspillerveiledning (LDN/LAN)", "pl_PL": "", - "pt_BR": "Guia multijogador (LDN/LAN)", + "pt_BR": "Guia Multijogador (LDN/LAN)", "ru_RU": "Гайд по мультиплееру (LDN/LAN)", "sv_SE": "Flerspelarguide (LDN/LAN)", "th_TH": "", @@ -1538,11 +1563,11 @@ "no_NO": "Utviklet av {0}", "pl_PL": "", "pt_BR": "Desenvolvido por {0}", - "ru_RU": "", - "sv_SE": "", + "ru_RU": "Разработана {0}", + "sv_SE": "Utvecklat av {0}", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Розроблено: {0}", "zh_CN": "由 {0} 开发", "zh_TW": "" } @@ -1587,7 +1612,7 @@ "ko_KR": "플레이 타임:", "no_NO": "Spilletid:", "pl_PL": "Czas w grze:", - "pt_BR": "Tempo de jogo:", + "pt_BR": "Tempo de Jogo:", "ru_RU": "Время в игре:", "sv_SE": "Speltid:", "th_TH": "เล่นไปแล้ว:", @@ -1612,7 +1637,7 @@ "ko_KR": "마지막 플레이:", "no_NO": "Sist Spilt:", "pl_PL": "Ostatnio grane:", - "pt_BR": "Último jogo:", + "pt_BR": "Última vez Jogado:", "ru_RU": "Последний запуск:", "sv_SE": "Senast spelad:", "th_TH": "เล่นล่าสุด:", @@ -1712,7 +1737,7 @@ "ko_KR": "플레이 타임", "no_NO": "Spilletid", "pl_PL": "Czas w grze:", - "pt_BR": "Tempo de jogo", + "pt_BR": "Tempo de Jogo", "ru_RU": "Время в игре", "sv_SE": "Speltid", "th_TH": "เล่นไปแล้ว", @@ -1737,7 +1762,7 @@ "ko_KR": "마지막 플레이", "no_NO": "Sist Spilt", "pl_PL": "Ostatnio grane", - "pt_BR": "Último jogo", + "pt_BR": "Última vez Jogado", "ru_RU": "Последний запуск", "sv_SE": "Senast spelad", "th_TH": "เล่นล่าสุด", @@ -1838,11 +1863,11 @@ "no_NO": "Kompatibilitet", "pl_PL": "", "pt_BR": "Compatibilidade:", - "ru_RU": "", - "sv_SE": "", + "ru_RU": "Совместимость:", + "sv_SE": "Kompatibilitet:", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Сумісність:", "zh_CN": "兼容性:", "zh_TW": "" } @@ -1862,12 +1887,12 @@ "ko_KR": "", "no_NO": "Tittel ID:", "pl_PL": "", - "pt_BR": "ID do título:", - "ru_RU": "", - "sv_SE": "", + "pt_BR": "ID do Título:", + "ru_RU": "ID приложения", + "sv_SE": "Titel-id:", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "ID гри:", "zh_CN": "标题 ID:", "zh_TW": "" } @@ -1887,12 +1912,12 @@ "ko_KR": "", "no_NO": "Spill som Arrangeres: {0}", "pl_PL": "", - "pt_BR": "Jogos hospedados: {0}", - "ru_RU": "", - "sv_SE": "", + "pt_BR": "Jogos Hospedados: {0}", + "ru_RU": "Запущенно игр: {0}", + "sv_SE": "Värdskap för spel: {0}", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Розміщені ігри: {0}", "zh_CN": "服务的游戏: {0}", "zh_TW": "" } @@ -1913,11 +1938,11 @@ "no_NO": "Online-spillere: {0}", "pl_PL": "", "pt_BR": "Jogadores Online: {0}", - "ru_RU": "", - "sv_SE": "", + "ru_RU": "Игроков онлайн: {0}", + "sv_SE": "Online-spelare: {0}", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Гравців онлайн: {0}", "zh_CN": "在线玩家: {0}", "zh_TW": "" } @@ -1937,7 +1962,7 @@ "ko_KR": "사용자 저장 디렉터리 열기", "no_NO": "Åpne bruker lagrings mappe", "pl_PL": "Otwórz katalog zapisów użytkownika", - "pt_BR": "Abrir diretório de saves do usuário", + "pt_BR": "Abrir Diretório de Saves do Usuário", "ru_RU": "Открыть папку с сохранениями", "sv_SE": "Öppna användarkatalog för sparningar", "th_TH": "เปิดไดเร็กทอรี่บันทึกของผู้ใช้", @@ -1987,7 +2012,7 @@ "ko_KR": "기기 저장 디렉터리 열기", "no_NO": "Åpne lagringsmappe for enheten", "pl_PL": "Otwórz katalog zapisów urządzenia", - "pt_BR": "Abrir diretório de saves de dispositivo do usuário", + "pt_BR": "Abrir Diretório de Saves de Dispositivo do Usuário", "ru_RU": "Открыть папку сохраненных устройств", "sv_SE": "Öppna enhetens katalog för sparade spel", "th_TH": "เปิดไดเร็กทอรี่บันทึกของอุปกรณ์", @@ -2037,7 +2062,7 @@ "ko_KR": "BCAT 저장 디렉터리 열기", "no_NO": "Åpne BCAT lagringsmappe", "pl_PL": "Otwórz katalog zapisu BCAT obecnego użytkownika", - "pt_BR": "Abrir diretório de saves BCAT do usuário", + "pt_BR": "Abrir Diretório de Saves BCAT do Usuário", "ru_RU": "Открыть папку сохраненных BCAT", "sv_SE": "Öppna katalog för BCAT-sparningar", "th_TH": "เปิดไดเรกทอรี่บันทึกของ BCAT", @@ -2087,7 +2112,7 @@ "ko_KR": "타이틀 업데이트 관리", "no_NO": "Administrer titteloppdateringer", "pl_PL": "Zarządzaj aktualizacjami", - "pt_BR": "Gerenciar atualizações do jogo", + "pt_BR": "Gerenciar Atualizações do Jogo", "ru_RU": "Управление обновлениями", "sv_SE": "Hantera speluppdateringar", "th_TH": "จัดการเวอร์ชั่นอัปเดต", @@ -2187,7 +2212,7 @@ "ko_KR": "캐시 관리", "no_NO": "Cache administrasjon", "pl_PL": "Zarządzanie Cache", - "pt_BR": "Gerenciamento de cache", + "pt_BR": "Gerenciamento de Cache", "ru_RU": "Управление кэшем", "sv_SE": "Cachehantering", "th_TH": "จัดการแคช", @@ -2212,7 +2237,7 @@ "ko_KR": "대기열 PPTC 재구성", "no_NO": "Start PPTC gjenoppbygging", "pl_PL": "Zakolejkuj rekompilację PPTC", - "pt_BR": "Limpar cache PPTC", + "pt_BR": "Reconstruir Cache PPTC", "ru_RU": "Перестроить очередь PPTC", "sv_SE": "Kölägg PPTC Rebuild", "th_TH": "เพิ่มคิวการสร้าง PPTC ใหม่", @@ -2237,7 +2262,7 @@ "ko_KR": "다음 게임 실행 부팅 시, PPTC를 트리거하여 다시 구성", "no_NO": "Utløs PPTC for å gjenoppbygge ved oppstart av neste spill-start", "pl_PL": "Zainicjuj Rekompilację PPTC przy następnym uruchomieniu gry", - "pt_BR": "Deleta o cache PPTC armazenado em disco do jogo", + "pt_BR": "Aciona o PPTC para reconstruir o cache no momento da próxima inicialização do jogo", "ru_RU": "Запускает перестройку PPTC во время следующего запуска игры.", "sv_SE": "Gör så att PPTC bygger om vid uppstart när nästa spel startas", "th_TH": "ให้ PPTC สร้างใหม่ในเวลาบูตเมื่อเปิดเกมครั้งถัดไป", @@ -2262,12 +2287,12 @@ "ko_KR": "PPTC 캐시 제거", "no_NO": "Tøm PPTC-bufferen", "pl_PL": "", - "pt_BR": "Limpar cache PPTC", - "ru_RU": "", - "sv_SE": "", + "pt_BR": "Limpar Cache PPTC", + "ru_RU": "Очистить кэш PPTC", + "sv_SE": "Rensa PPTC-cache", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Очистити кеш PPTC", "zh_CN": "清理 PPTC 缓存", "zh_TW": "" } @@ -2287,12 +2312,12 @@ "ko_KR": "앱의 모든 PPTC 캐시 파일 삭제", "no_NO": "Sletter alle PPTC-cache-filer for applikasjonen", "pl_PL": "", - "pt_BR": "Apagar os arquivos de cache PPTC do aplicativo", - "ru_RU": "", - "sv_SE": "", + "pt_BR": "Apaga os arquivos de cache PPTC do aplicativo", + "ru_RU": "Удаляет все файлы кэша PPTC для приложения", + "sv_SE": "Tar bort alla PPTC-cachefiler för applikationen", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Видаляє всі файли кешу PPTC для застосунку", "zh_CN": "删除应用程序的所有 PPTC 缓存", "zh_TW": "" } @@ -2312,7 +2337,7 @@ "ko_KR": "퍼지 셰이더 캐시", "no_NO": "Tøm shader cache", "pl_PL": "Wyczyść pamięć podręczną cieni", - "pt_BR": "Limpar cache de Shader", + "pt_BR": "Limpar Cache de Shader", "ru_RU": "Очистить кэш шейдеров", "sv_SE": "Töm shader cache", "th_TH": "ล้างแคช แสงเงา", @@ -2337,7 +2362,7 @@ "ko_KR": "앱의 셰이더 캐시 삭제", "no_NO": "Sletter applikasjonens shader cache", "pl_PL": "Usuwa pamięć podręczną cieni danej aplikacji", - "pt_BR": "Deleta o cache de Shader armazenado em disco do jogo", + "pt_BR": "Deleta o cache de Shader do jogo armazenado em disco", "ru_RU": "Удаляет кеш шейдеров приложения", "sv_SE": "Tar bort applikationens shader cache", "th_TH": "ลบแคช แสงเงา ของแอปพลิเคชัน", @@ -2362,7 +2387,7 @@ "ko_KR": "PPTC 디렉터리 열기", "no_NO": "Åpne PPTC mappe", "pl_PL": "Otwórz katalog PPTC", - "pt_BR": "Abrir diretório do cache PPTC", + "pt_BR": "Abrir Diretório de PPTC Cache", "ru_RU": "Открыть папку PPTC", "sv_SE": "Öppna PPTC-katalog", "th_TH": "เปิดไดเรกทอรี่ PPTC", @@ -2412,7 +2437,7 @@ "ko_KR": "셰이더 캐시 디렉터리 열기", "no_NO": "Åpne Shader Cache-mappen", "pl_PL": "Otwórz katalog pamięci podręcznej cieni", - "pt_BR": "Abrir diretório do cache de Shader", + "pt_BR": "Abrir Diretório de Shader Cache", "ru_RU": "Открыть папку с кэшем шейдеров", "sv_SE": "Öppna katalog för shader cache", "th_TH": "เปิดไดเรกทอรี่ แคช แสงเงา", @@ -2462,7 +2487,7 @@ "ko_KR": "데이터 추출", "no_NO": "Hent ut data", "pl_PL": "Wypakuj dane", - "pt_BR": "Extrair dados", + "pt_BR": "Extrair Dados", "ru_RU": "Извлечь данные", "sv_SE": "Extrahera data", "th_TH": "แยกส่วนข้อมูล", @@ -2613,11 +2638,11 @@ "no_NO": "Pakk ut RomFS filene fra valgt DLC fil", "pl_PL": "", "pt_BR": "Extraia o RomFS de um arquivo DLC selecionado", - "ru_RU": "", + "ru_RU": "Извлекает файлы RomFS из выбранного файла DLC", "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": "" } @@ -2687,7 +2712,7 @@ "ko_KR": "바로 가기 만들기", "no_NO": "Lag programsnarvei", "pl_PL": "Utwórz skrót aplikacji", - "pt_BR": "Criar atalho da aplicação", + "pt_BR": "Criar Atalho da Aplicação", "ru_RU": "Создать ярлык приложения", "sv_SE": "Skapa genväg till applikation", "th_TH": "สร้างทางลัดของแอปพลิเคชัน", @@ -2712,7 +2737,7 @@ "ko_KR": "선택한 앱을 실행하는 바탕 화면에 바로 가기를 생성", "no_NO": "Lag en snarvei på skrivebordet som starter den valgte Applikasjonen", "pl_PL": "Utwórz skrót na pulpicie, który uruchamia wybraną aplikację", - "pt_BR": "Criar um atalho de área de trabalho que inicia o aplicativo selecionado", + "pt_BR": "Criar um atalho na área de trabalho que inicia o aplicativo selecionado", "ru_RU": "Создает ярлык на рабочем столе, с помощью которого можно запустить игру или приложение", "sv_SE": "Skapa en skrivbordsgenväg som startar vald applikation", "th_TH": "สร้างทางลัดบนเดสก์ท็อปสำหรับใช้แอปพลิเคชันที่เลือก", @@ -2722,6 +2747,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": { @@ -2747,6 +2822,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": { @@ -2762,12 +2887,12 @@ "ko_KR": "호환성 항목 표시", "no_NO": "Vis kompatibilitetsoppføring", "pl_PL": "", - "pt_BR": "Mostrar entrada de compatibilidade", - "ru_RU": "", - "sv_SE": "", + "pt_BR": "Mostrar Dados de Compatibilidade", + "ru_RU": "Показать записи о совместимости", + "sv_SE": "Visa kompatibilitetspost", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Iнформація про сумісність", "zh_CN": "显示兼容性项目", "zh_TW": "" } @@ -2788,11 +2913,11 @@ "no_NO": "Vis det valgte spillet i kompatibilitetslisten, som du vanligvis får tilgang til via Hjelp-menyen.", "pl_PL": "", "pt_BR": "Exibe o jogo selecionado na Lista de Compatibilidade, que normalmente pode ser acessada pelo menu Ajuda.", - "ru_RU": "", - "sv_SE": "", + "ru_RU": "Отобразить выбранную игру в списке совместимости, доступ к которому вы обычно можете получить через меню Справки.", + "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": "" } @@ -2812,12 +2937,12 @@ "ko_KR": "게임 통계 표시", "no_NO": "Vis Spill Info", "pl_PL": "", - "pt_BR": "Mostrar informações do jogo", - "ru_RU": "", - "sv_SE": "", + "pt_BR": "Mostrar Informações do Jogo", + "ru_RU": "Показать информацию об игре", + "sv_SE": "Visa spelinformation", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Інформація про гру", "zh_CN": "显示游戏信息", "zh_TW": "" } @@ -2837,12 +2962,12 @@ "ko_KR": "그리드 보기 레이아웃에서 누락된 현재 선택된 게임에 대한 다양한 정보를 표시합니다.", "no_NO": "Vis statistikk og detaljer om det valgte spillet.", "pl_PL": "", - "pt_BR": "Mostrar estatísticas e detalhes sobre o jogo selecionado no momento.", - "ru_RU": "", - "sv_SE": "", + "pt_BR": "Exibe estatísticas e detalhes sobre o jogo selecionado.", + "ru_RU": "Показывать статистику и подробную информацию о выбранной игре.", + "sv_SE": "Visa statistik och detaljer om det aktuella spelet.", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Показати статистику та деталі обраної гри.", "zh_CN": "显示当前选定游戏的状态与详细信息。", "zh_TW": "" } @@ -2862,7 +2987,7 @@ "ko_KR": "모드 디렉터리 열기", "no_NO": "Åpne Modifikasjonsmappen", "pl_PL": "Otwórz katalog modów", - "pt_BR": "Abrir pasta de Mods", + "pt_BR": "Abrir Pasta de Mods", "ru_RU": "Открыть папку с модами", "sv_SE": "Öppna Mods-katalog", "th_TH": "เปิดไดเร็กทอรี่ Mods", @@ -2912,7 +3037,7 @@ "ko_KR": "Atmosphere 모드 디렉터리 열기", "no_NO": "Åpne Atmosfære modifikasjons mappen", "pl_PL": "Otwórz katalog modów Atmosphere", - "pt_BR": "Abrir diretório de mods Atmosphere", + "pt_BR": "Abrir Diretório de Mods Atmosphere", "ru_RU": "Открыть папку с модами Atmosphere", "sv_SE": "Öppna Atmosphere Mods-katalogen", "th_TH": "เปิดไดเร็กทอรี่ Mods Atmosphere", @@ -2962,7 +3087,7 @@ "ko_KR": "XCI 파일 확인 및 트림", "no_NO": "Kontroller og trim XCI-filen", "pl_PL": "", - "pt_BR": "Verificar e reduzir o arquivo XCI", + "pt_BR": "Verificar e Reduzir o Arquivo XCI", "ru_RU": "Проверить и обрезать XCI файл", "sv_SE": "Kontrollera och optimera XCI-fil", "th_TH": "", @@ -3012,7 +3137,7 @@ "ko_KR": "{0}/{1}개의 게임 불러옴", "no_NO": "{0}/{1} Spill Lastet", "pl_PL": "{0}/{1} Załadowane gry", - "pt_BR": "{0}/{1} jogos carregados", + "pt_BR": "{0}/{1} Jogos Carregados", "ru_RU": "{0}/{1} игр загружено", "sv_SE": "{0}/{1} spel inlästa", "th_TH": "เกมส์โหลดแล้ว {0}/{1}", @@ -3037,7 +3162,7 @@ "ko_KR": "펌웨어 버전 : {0}", "no_NO": "Fastvareversjon: {0}", "pl_PL": "", - "pt_BR": "Versão do firmware: {0}", + "pt_BR": "Versão do Firmware: {0}", "ru_RU": "Версия прошивки: {0}", "sv_SE": "Firmware-version: {0}", "th_TH": "", @@ -3062,7 +3187,7 @@ "ko_KR": "XCI 파일 '{0}' 트리밍", "no_NO": "Trimming av XCI-filen '{0}'", "pl_PL": "", - "pt_BR": "Reduzindo arquivo XCI '{0}'", + "pt_BR": "Reduzindo o Arquivo XCI '{0}'", "ru_RU": "Обрезается XCI файл '{0}'", "sv_SE": "Optimerar XCI-filen '{0}'", "th_TH": "", @@ -3087,7 +3212,7 @@ "ko_KR": "메모리 매핑 한계 감지", "no_NO": "Lav grense for minnetildelinger oppdaget", "pl_PL": "Wykryto niski limit dla przypisań pamięci", - "pt_BR": "Limite baixo para mapeamentos de memória detectado", + "pt_BR": "Detectado limite baixo para mapeamentos de memória", "ru_RU": "Обнаружен низкий лимит разметки памяти", "sv_SE": "Låg gräns för minnesmappningar upptäcktes", "th_TH": "การตั้งค่าหน่วยความถึงขีดจำกัดต่ำสุดแล้ว", @@ -3272,6 +3397,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": { @@ -3287,7 +3437,7 @@ "ko_KR": "사용자 인터페이스", "no_NO": "Brukergrensesnitt", "pl_PL": "Interfejs użytkownika", - "pt_BR": "Geral", + "pt_BR": "Interface do Usuário", "ru_RU": "Интерфейс", "sv_SE": "Användargränssnitt", "th_TH": "หน้าจอผู้ใช้", @@ -3337,7 +3487,7 @@ "ko_KR": "디스코드 활동 상태 활성화", "no_NO": "Aktiver Discord Rik Tilstedeværelse", "pl_PL": "Włącz Bogatą Obecność Discord", - "pt_BR": "Habilitar Rich Presence do Discord", + "pt_BR": "Habilitar Presença no Discord", "ru_RU": "Статус активности в Discord", "sv_SE": "Aktivera Discord Rich Presence", "th_TH": "เปิดใช้งาน Discord Rich Presence", @@ -3362,12 +3512,12 @@ "ko_KR": "", "no_NO": "Se etter Oppdateringer:", "pl_PL": "", - "pt_BR": "Verificar atualizações:", - "ru_RU": "", - "sv_SE": "", + "pt_BR": "Verificar Atualizações:", + "ru_RU": "Проверка наличия обновлений", + "sv_SE": "Leta efter uppdateringar:", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Перевірка оновлень:", "zh_CN": "检查更新", "zh_TW": "" } @@ -3388,11 +3538,11 @@ "no_NO": "Av", "pl_PL": "", "pt_BR": "Desligado", - "ru_RU": "", - "sv_SE": "", + "ru_RU": "Отключить", + "sv_SE": "Av", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Вимкнути", "zh_CN": "关闭", "zh_TW": "" } @@ -3412,12 +3562,12 @@ "ko_KR": "", "no_NO": "Spør", "pl_PL": "", - "pt_BR": "", - "ru_RU": "", - "sv_SE": "", + "pt_BR": "Ao Abrir", + "ru_RU": "При запуске", + "sv_SE": "Fråga", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Запитувати щоразу", "zh_CN": "提示", "zh_TW": "" } @@ -3437,12 +3587,12 @@ "ko_KR": "", "no_NO": "Bakgrunn", "pl_PL": "", - "pt_BR": "Fundo", - "ru_RU": "", - "sv_SE": "", + "pt_BR": "2° Plano", + "ru_RU": "В фоне", + "sv_SE": "Bakgrund", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Оновлювати в фоні", "zh_CN": "背景", "zh_TW": "" } @@ -3462,12 +3612,12 @@ "ko_KR": "", "no_NO": "På Emulator Fokus Tapt:", "pl_PL": "", - "pt_BR": "Ao perder o Foco do emulador:", - "ru_RU": "", - "sv_SE": "", + "pt_BR": "Ao Perder o Foco:", + "ru_RU": "При выходе эмулятора из фокуса", + "sv_SE": "När emulatorn tappar fokus:", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "При втраті фокуса емулятором:", "zh_CN": "当模拟器在后台时:", "zh_TW": "" } @@ -3487,12 +3637,12 @@ "ko_KR": "", "no_NO": "Gjør Ingenting", "pl_PL": "", - "pt_BR": "Não fazer nada", - "ru_RU": "", - "sv_SE": "", + "pt_BR": "Não Fazer Nada", + "ru_RU": "Ничего не делать", + "sv_SE": "Gör ingenting", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Нічого не робити", "zh_CN": "什么事情也不做", "zh_TW": "" } @@ -3512,12 +3662,12 @@ "ko_KR": "", "no_NO": "Blokkinngang", "pl_PL": "", - "pt_BR": "Bloquear entrada", - "ru_RU": "", - "sv_SE": "", + "pt_BR": "Bloquear Controles", + "ru_RU": "Блокировать управление", + "sv_SE": "Blockera inmatning", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Блокувати введення", "zh_CN": "禁用输入", "zh_TW": "" } @@ -3537,12 +3687,12 @@ "ko_KR": "", "no_NO": "Demp Lyd", "pl_PL": "", - "pt_BR": "Ficar mudo", - "ru_RU": "", - "sv_SE": "", + "pt_BR": "Ficar Mudo", + "ru_RU": "Отключить звук", + "sv_SE": "Stäng av ljudet", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Вимкнути звук", "zh_CN": "静音", "zh_TW": "" } @@ -3562,12 +3712,12 @@ "ko_KR": "", "no_NO": "Blokker Inputs og demp Volumet", "pl_PL": "", - "pt_BR": "Bloquear entrada & Ficar mudo", - "ru_RU": "", - "sv_SE": "", + "pt_BR": "Bloquear Controles & Ficar Mudo", + "ru_RU": "Блокировать управление и отключить звук", + "sv_SE": "Blockera inmatningar och stäng av ljudet", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Блокувати введення та Вимкнути звук", "zh_CN": "阻止输入且静音", "zh_TW": "" } @@ -3587,12 +3737,12 @@ "ko_KR": "", "no_NO": "Pause Emulatoren", "pl_PL": "", - "pt_BR": "Pausar a emulação", - "ru_RU": "", - "sv_SE": "", + "pt_BR": "Pausar a Emulação", + "ru_RU": "Поставить паузу", + "sv_SE": "Pausa emuleringen", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Поставити на паузу", "zh_CN": "暂停模拟", "zh_TW": "" } @@ -3612,7 +3762,7 @@ "ko_KR": "\"종료 확인\" 대화 상자 표시", "no_NO": "Vis \"Bekreft Avslutt\" vinduet", "pl_PL": "Pokazuj okno dialogowe \"Potwierdź wyjście\"", - "pt_BR": "Exibir diálogo de confirmação ao sair", + "pt_BR": "Exibir \"Diálogo de confirmação\" ao Sair", "ru_RU": "Подтверждать выход из приложения", "sv_SE": "Visa \"Bekräfta avslut\"-dialog", "th_TH": "แสดง \"ปุ่มยืนยันการออก\" เมื่อออกเกม", @@ -3637,7 +3787,7 @@ "ko_KR": "창 크기/위치 기억", "no_NO": "Husk vinduets størrelse/posisjon", "pl_PL": "", - "pt_BR": "Lembrar tamanho/posição da Janela", + "pt_BR": "Lembrar Tamanho e Posição da Janela", "ru_RU": "Запомнить размер/положение окна", "sv_SE": "Kom ihåg fönstrets storlek/position", "th_TH": "จดจำ ขนาดหน้าต่างแอพพลิเคชั่น/คำแหน่ง", @@ -3662,9 +3812,9 @@ "ko_KR": "", "no_NO": "Deaktiver inndata når vinduet er ute av fokus", "pl_PL": "", - "pt_BR": "Desativar entrada quando estiver fora de foco", - "ru_RU": "", - "sv_SE": "", + "pt_BR": "Desativar Controles Quando Estiver Fora de Foco", + "ru_RU": "Отключает управление при выходе из фокуса", + "sv_SE": "Inaktivera inmatning när fokus tappas", "th_TH": "", "tr_TR": "", "uk_UA": "", @@ -3687,7 +3837,7 @@ "ko_KR": "제목 표시줄 표시(다시 시작해야 함)", "no_NO": "Vis tittellinje (krever omstart)", "pl_PL": "", - "pt_BR": "Mostrar barra de título (requer reinicialização)", + "pt_BR": "Mostrar Barra de Título (Requer reinicialização)", "ru_RU": "Показать строку заголовка (требуется перезапуск)", "sv_SE": "Visa titelrad (kräver omstart)", "th_TH": "", @@ -3712,12 +3862,12 @@ "ko_KR": "커서 숨기기 :", "no_NO": "Skjul musepeker:", "pl_PL": "Ukryj kursor:", - "pt_BR": "Esconder o cursor do mouse:", + "pt_BR": "Esconder Cursor do Mouse:", "ru_RU": "Скрывать курсор", "sv_SE": "Dölj markör:", "th_TH": "ซ่อน เคอร์เซอร์:", "tr_TR": "İşaretçiyi Gizle:", - "uk_UA": "Сховати вказівник:", + "uk_UA": "Сховати курсор:", "zh_CN": "隐藏鼠标指针:", "zh_TW": "隱藏滑鼠游標:" } @@ -3762,7 +3912,7 @@ "ko_KR": "유휴 상태", "no_NO": "Når inaktiv", "pl_PL": "Gdy bezczynny", - "pt_BR": "Esconder o cursor quando ocioso", + "pt_BR": "Quando Ocioso", "ru_RU": "В простое", "sv_SE": "Vid overksam", "th_TH": "เมื่อไม่ได้ใช้งาน", @@ -3812,7 +3962,7 @@ "ko_KR": "게임 데릭터리", "no_NO": "Spillmapper", "pl_PL": "Katalogi gier", - "pt_BR": "Diretórios de jogo", + "pt_BR": "Pasta de Jogos", "ru_RU": "Папки с играми", "sv_SE": "Spelkataloger", "th_TH": "ไดเรกทอรี่ของเกม", @@ -3837,7 +3987,7 @@ "ko_KR": "DLC/업데이트 디렉터리 자동 불러오기", "no_NO": "Autoload DLC/Updates-mapper", "pl_PL": "", - "pt_BR": "Carregar Automaticamente Diretórios de DLC/Atualizações", + "pt_BR": "Carregar Automaticamente Pasta de DLC e Atualizações", "ru_RU": "Автозагрузка папки с DLC/Обновлениями", "sv_SE": "Läs automatisk in DLC/speluppdateringar", "th_TH": "โหลดไดเรกทอรี DLC/ไฟล์อัปเดต อัตโนมัติ", @@ -3862,7 +4012,7 @@ "ko_KR": "누락된 파일을 참조하는 DLC 및 업데이트가 자동으로 불러오기 취소", "no_NO": "DLC og oppdateringer som henviser til manglende filer, vil bli lastet ned automatisk", "pl_PL": "", - "pt_BR": "DLCs e Atualizações que se referem a arquivos ausentes serão descarregadas automaticamente", + "pt_BR": "DLCs e Atualizações que se referem a arquivos ausentes serão desabilitados automaticamente", "ru_RU": "DLC и обновления, которые ссылаются на отсутствующие файлы, будут выгружаться автоматически", "sv_SE": "DLC och speluppdateringar som refererar till saknade filer kommer inte att läsas in automatiskt", "th_TH": "", @@ -3987,7 +4137,7 @@ "ko_KR": "시스템 지역 :", "no_NO": "System region:", "pl_PL": "Region systemu:", - "pt_BR": "Região do sistema:", + "pt_BR": "Região do Sistema:", "ru_RU": "Регион прошивки:", "sv_SE": "Systemregion:", "th_TH": "ภูมิภาคของระบบ:", @@ -4187,7 +4337,7 @@ "ko_KR": "시스템 언어 :", "no_NO": "Systemspråk", "pl_PL": "Język systemu:", - "pt_BR": "Idioma do sistema:", + "pt_BR": "Idioma do Sistema:", "ru_RU": "Язык системы:", "sv_SE": "Systemspråk:", "th_TH": "ภาษาของระบบ:", @@ -4237,7 +4387,7 @@ "ko_KR": "미국 영어", "no_NO": "Amerikansk engelsk", "pl_PL": "Angielski (Stany Zjednoczone)", - "pt_BR": "Inglês americano", + "pt_BR": "Inglês Americano", "ru_RU": "Английский (США)", "sv_SE": "Amerikansk engelska", "th_TH": "อังกฤษ (อเมริกัน)", @@ -4512,7 +4662,7 @@ "ko_KR": "영국 영어", "no_NO": "Britisk engelsk", "pl_PL": "Angielski (Wielka Brytania)", - "pt_BR": "Inglês britânico", + "pt_BR": "Inglês Britânico", "ru_RU": "Английский (Великобритания)", "sv_SE": "Brittisk engelska", "th_TH": "อังกฤษ (บริติช)", @@ -4537,7 +4687,7 @@ "ko_KR": "캐나다 프랑스어", "no_NO": "Canadisk Fransk", "pl_PL": "Kanadyjski Francuski", - "pt_BR": "Francês canadense", + "pt_BR": "Francês Canadense", "ru_RU": "Французский (Канада)", "sv_SE": "Kanadensisk franska", "th_TH": "ฝรั่งเศส (แคนาดา)", @@ -4562,7 +4712,7 @@ "ko_KR": "남미 스페인어", "no_NO": "Latinamerikansk spansk", "pl_PL": "Hiszpański (Ameryka Łacińska)", - "pt_BR": "Espanhol latino", + "pt_BR": "Espanhol Latino", "ru_RU": "Испанский (Латинская Америка)", "sv_SE": "Latinamerikansk spanska", "th_TH": "สเปน (ลาตินอเมริกา)", @@ -4587,7 +4737,7 @@ "ko_KR": "중국어 간체", "no_NO": "Forenklet kinesisk", "pl_PL": "Chiński (Uproszczony)", - "pt_BR": "Chinês simplificado", + "pt_BR": "Chinês Simplificado", "ru_RU": "Китайский (упрощённый)", "sv_SE": "Förenklad kinesiska", "th_TH": "จีน (ตัวย่อ)", @@ -4612,7 +4762,7 @@ "ko_KR": "중국어 번체", "no_NO": "Tradisjonell Kinesisk", "pl_PL": "Chiński (Tradycyjny)", - "pt_BR": "Chinês tradicional", + "pt_BR": "Chinês Tradicional", "ru_RU": "Китайский (традиционный)", "sv_SE": "Traditionell kinesiska", "th_TH": "จีน (ดั้งเดิม)", @@ -4621,56 +4771,6 @@ "zh_CN": "繁体中文(推荐)", "zh_TW": "正體中文 (建議)" } - }, - { - "ID": "SettingsTabSystemSystemLanguageSwedish", - "Translations": { - "ar_SA": "", - "de_DE": "", - "el_GR": "", - "en_US": "Swedish", - "es_ES": "", - "fr_FR": "", - "he_IL": "", - "it_IT": "", - "ja_JP": "", - "ko_KR": "스웨덴어", - "no_NO": "Svensk", - "pl_PL": "", - "pt_BR": "", - "ru_RU": "", - "sv_SE": "Svenska", - "th_TH": "", - "tr_TR": "", - "uk_UA": "", - "zh_CN": "瑞典语", - "zh_TW": "" - } - }, - { - "ID": "SettingsTabSystemSystemLanguageNorwegian", - "Translations": { - "ar_SA": "", - "de_DE": "", - "el_GR": "", - "en_US": "Norwegian", - "es_ES": "", - "fr_FR": "", - "he_IL": "", - "it_IT": "", - "ja_JP": "", - "ko_KR": "노르웨이어", - "no_NO": "Norsk", - "pl_PL": "", - "pt_BR": "", - "ru_RU": "", - "sv_SE": "Norska", - "th_TH": "", - "tr_TR": "", - "uk_UA": "", - "zh_CN": "挪威语", - "zh_TW": "" - } }, { "ID": "SettingsTabSystemSystemTimeZone", @@ -4687,7 +4787,7 @@ "ko_KR": "시스템 시간대 :", "no_NO": "System Tidssone:", "pl_PL": "Strefa czasowa systemu:", - "pt_BR": "Fuso horário do sistema:", + "pt_BR": "Fuso Horário do Sistema:", "ru_RU": "Часовой пояс системы:", "sv_SE": "Systemets tidszon:", "th_TH": "เขตเวลาของระบบ:", @@ -4712,7 +4812,7 @@ "ko_KR": "시스템 시간 :", "no_NO": "System tid:", "pl_PL": "Czas systemu:", - "pt_BR": "Hora do sistema:", + "pt_BR": "Data e Hora do Sistema:", "ru_RU": "Системное время:", "sv_SE": "Systemtid:", "th_TH": "เวลาของระบบ:", @@ -4737,9 +4837,9 @@ "ko_KR": "매치 시스템 시간", "no_NO": "Match systemtid", "pl_PL": "", - "pt_BR": "Sincronizar data e hora com o sistema PC", - "ru_RU": "", - "sv_SE": "", + "pt_BR": "Sincronizar com o Sistema PC", + "ru_RU": "Соответствовать времени в системе", + "sv_SE": "Matcha systemtid", "th_TH": "", "tr_TR": "", "uk_UA": "", @@ -4787,7 +4887,7 @@ "ko_KR": "저전력 PPTC 캐시", "no_NO": "PPTC med lavt strømforbruk", "pl_PL": "Low-power PPTC", - "pt_BR": "Cache PPTC com baixo consumo de energia", + "pt_BR": "Cache PPTC com Baixo Consumo de Energia", "ru_RU": "PPTC с низким электропотреблением", "sv_SE": "PPTC med låg strömförbrukning", "th_TH": "PPTC แบบพลังงานตํ่า", @@ -4812,7 +4912,7 @@ "ko_KR": "파일 시스템 무결성 검사", "no_NO": "FS Integritetssjekk", "pl_PL": "Sprawdzanie integralności systemu plików", - "pt_BR": "Habilitar verificação de integridade do sistema de arquivos", + "pt_BR": "Verificações de Integridade do FS", "ru_RU": "Проверка целостности файловой системы", "sv_SE": "Integritetskontroller av filsystem", "th_TH": "ตรวจสอบความถูกต้องของ FS", @@ -4837,7 +4937,7 @@ "ko_KR": "음향 후단부 :", "no_NO": "Lyd Backend:", "pl_PL": "Backend Dżwięku:", - "pt_BR": "Biblioteca de saída de áudio:", + "pt_BR": "Módulo de Áudio:", "ru_RU": "Аудио бэкенд:", "sv_SE": "Ljudbakände:", "th_TH": "ระบบเสียงเบื้องหลัง:", @@ -4862,7 +4962,7 @@ "ko_KR": "더미", "no_NO": "", "pl_PL": "Atrapa", - "pt_BR": "Nenhuma", + "pt_BR": "Nenhum", "ru_RU": "Без звука", "sv_SE": "", "th_TH": "", @@ -4913,7 +5013,7 @@ "no_NO": "Lyd Inn/Ut", "pl_PL": "", "pt_BR": "", - "ru_RU": "", + "ru_RU": "Выход/Вход звука", "sv_SE": "", "th_TH": "", "tr_TR": "", @@ -4987,7 +5087,7 @@ "ko_KR": "불안정성을 유발할 수 있음", "no_NO": "Kan forårsake ustabilitet", "pl_PL": " (mogą powodować niestabilność)", - "pt_BR": " (Pode causar instabilidade)", + "pt_BR": "Pode causar instabilidade", "ru_RU": "Возможна нестабильная работа", "sv_SE": "Kan orsaka instabilitet", "th_TH": "อาจทำให้เกิดข้อผิดพลาดได้", @@ -5137,7 +5237,7 @@ "ko_KR": "누락된 서비스 무시", "no_NO": "Ignorer manglende tjenester", "pl_PL": "Ignoruj Brakujące Usługi", - "pt_BR": "Ignorar serviços não implementados", + "pt_BR": "Ignorar Serviços Ausentes", "ru_RU": "Игнорировать отсутствующие службы", "sv_SE": "Ignorera saknade tjänster", "th_TH": "เมินเฉยบริการที่หายไป", @@ -5148,28 +5248,28 @@ } }, { - "ID": "SettingsTabSystemIgnoreApplet", + "ID": "SettingsTabSystemIgnoreControllerApplet", "Translations": { "ar_SA": "", - "de_DE": "Applet ignorieren", - "el_GR": "Αγνοήστε το Applet", - "en_US": "Ignore Applet", - "es_ES": "Ignorar el Applet", - "fr_FR": "Ignorer la déconnexion de la manette", + "de_DE": "", + "el_GR": "", + "en_US": "Ignore Controller Applet", + "es_ES": "", + "fr_FR": "", "he_IL": "", - "it_IT": "Ignora l'applet", - "ja_JP": "アプレットを無視する", - "ko_KR": "애플릿 무시", - "no_NO": "Ignorer appleten", - "pl_PL": "Ignoruj ​​aplet", - "pt_BR": "Ignorar applet", - "ru_RU": "Игнорировать Апплет", - "sv_SE": "Ignorera applet", - "th_TH": "เมินเฉย Applet", + "it_IT": "", + "ja_JP": "", + "ko_KR": "", + "no_NO": "", + "pl_PL": "", + "pt_BR": "Ignorar Applet do Controlador", + "ru_RU": "Игнорировать апплет контроллера", + "sv_SE": "Ignorera kontroller-applet", + "th_TH": "", "tr_TR": "", - "uk_UA": "Ігнорувати Аплет", - "zh_CN": "忽略小程序", - "zh_TW": "忽略小程式" + "uk_UA": "Ігнорувати Аплет Контролера", + "zh_CN": "忽略控制器小程序", + "zh_TW": "" } }, { @@ -5237,7 +5337,7 @@ "ko_KR": "셰이더 캐시 활성화", "no_NO": "Aktiver Shader Cachen", "pl_PL": "Włącz pamięć podręczną cieni", - "pt_BR": "Habilitar cache de shader", + "pt_BR": "Habilitar Cache de Shader", "ru_RU": "Кэшировать шейдеры", "sv_SE": "Aktivera Shader Cache", "th_TH": "เปิดใช้งาน แคชแสงเงา", @@ -5262,7 +5362,7 @@ "ko_KR": "이방성 필터링 :", "no_NO": "Anisotropisk filtrering:", "pl_PL": "Filtrowanie anizotropowe:", - "pt_BR": "Filtragem anisotrópica:", + "pt_BR": "Filtro Anisotrópico:", "ru_RU": "Анизотропная фильтрация:", "sv_SE": "Anisotropisk filtrering:", "th_TH": "ตัวกรองแบบ Anisotropic:", @@ -5412,7 +5512,7 @@ "ko_KR": "해상도 배율 :", "no_NO": "Oppløsnings skala:", "pl_PL": "Skalowanie rozdzielczości:", - "pt_BR": "Escala de resolução:", + "pt_BR": "Escala de Resolução:", "ru_RU": "Масштабирование:", "sv_SE": "Upplösningsskalning:", "th_TH": "อัตราส่วนความละเอียด:", @@ -5437,7 +5537,7 @@ "ko_KR": "사용자 정의(권장하지 않음)", "no_NO": "Egendefinert (anbefales ikke)", "pl_PL": "Niestandardowa (Niezalecane)", - "pt_BR": "Customizada (não recomendado)", + "pt_BR": "Customizada (Não recomendado)", "ru_RU": "Пользовательское (не рекомендуется)", "sv_SE": "Anpassad (rekommenderas inte)", "th_TH": "กำหนดเอง (ไม่แนะนำ)", @@ -5537,7 +5637,7 @@ "ko_KR": "4배(2880p/4320p) (권장하지 않음)", "no_NO": "4x (2880p/4320p) (anbefales ikke)", "pl_PL": "4x (2880p/4320p) (niezalecane)", - "pt_BR": "4x (2880p/4320p) (não recomendado)", + "pt_BR": "4x (2880p/4320p) (Não recomendado)", "ru_RU": "4x (2880p/4320p) (не рекомендуется)", "sv_SE": "4x (2880p/4320p) (rekommenderas inte)", "th_TH": "4x (2880p/4320p) (ไม่แนะนำ)", @@ -5562,7 +5662,7 @@ "ko_KR": "종횡비 :", "no_NO": "Bildeformat", "pl_PL": "Format obrazu:", - "pt_BR": "Proporção:", + "pt_BR": "Proporção da Tela:", "ru_RU": "Соотношение сторон:", "sv_SE": "Bildförhållande:", "th_TH": "อัตราส่วนภาพ:", @@ -5712,7 +5812,7 @@ "ko_KR": "창에 맞춰 늘리기", "no_NO": "Strekk for og Tilpasse vindu", "pl_PL": "Rozciągnij do Okna", - "pt_BR": "Esticar até caber", + "pt_BR": "Esticar até Caber", "ru_RU": "Растянуть до размеров окна", "sv_SE": "Sträck ut för att passa fönster", "th_TH": "ยืดภาพเพื่อให้พอดีกับหน้าต่าง", @@ -5762,7 +5862,7 @@ "ko_KR": "그래픽 셰이더 덤프 경로 :", "no_NO": "Grafikk Shader Dump bane:", "pl_PL": "Ścieżka do zgranych cieni graficznych:", - "pt_BR": "Diretório para despejo de shaders:", + "pt_BR": "Diretório para Despejo de Shaders:", "ru_RU": "Путь дампа графических шейдеров", "sv_SE": "Sökväg för Graphics Shader Dump:", "th_TH": "ที่เก็บ ดัมพ์ไฟล์ แสงเงา:", @@ -5837,7 +5937,7 @@ "ko_KR": "파일에 로그 기록 활성화", "no_NO": "Aktiver logging til fil", "pl_PL": "Włącz rejestrowanie zdarzeń do pliku", - "pt_BR": "Salvar logs em arquivo", + "pt_BR": "Salvar Logs em Arquivo", "ru_RU": "Включить запись в файл", "sv_SE": "Aktivera loggning till fil", "th_TH": "เปิดใช้งานการบันทึกประวัติ ไปยังไฟล์", @@ -5862,7 +5962,7 @@ "ko_KR": "조각 기록 활성화", "no_NO": "Aktiver Stub-logger", "pl_PL": "Wlącz Skróty Logów", - "pt_BR": "Habilitar logs de stub", + "pt_BR": "Habilitar logs de Stub", "ru_RU": "Включить журнал-заглушку", "sv_SE": "Aktivera stubbloggar", "th_TH": "เปิดใช้งานการบันทึกประวัติ", @@ -5887,7 +5987,7 @@ "ko_KR": "정보 기록 활성화", "no_NO": "Aktiver informasjonslogger", "pl_PL": "Włącz Logi Informacyjne", - "pt_BR": "Habilitar logs de informação", + "pt_BR": "Habilitar logs de Informação", "ru_RU": "Включить информационный журнал", "sv_SE": "Aktivera informationsloggar", "th_TH": "เปิดใช้งานการบันทึกประวัติการใช้งาน", @@ -5912,7 +6012,7 @@ "ko_KR": "경고 기록 활성화", "no_NO": "Aktiver varsellogger", "pl_PL": "Włącz Logi Ostrzeżeń", - "pt_BR": "Habilitar logs de alerta", + "pt_BR": "Habilitar Logs de Alerta", "ru_RU": "Включить журнал предупреждений", "sv_SE": "Aktivera varningsloggar", "th_TH": "เปิดใช้งานการบันทึกประวัติคำเตือน", @@ -5937,7 +6037,7 @@ "ko_KR": "오류 기록 활성화", "no_NO": "Aktiver feillogger", "pl_PL": "Włącz Logi Błędów", - "pt_BR": "Habilitar logs de erro", + "pt_BR": "Habilitar Logs de Erro", "ru_RU": "Включить журнал ошибок", "sv_SE": "Aktivera felloggar", "th_TH": "เปิดใช้งานการบันทึกประวัติข้อผิดพลาด", @@ -5962,7 +6062,7 @@ "ko_KR": "추적 기록 활성화", "no_NO": "Aktiver spor logger", "pl_PL": "Włącz Logi Śledzenia", - "pt_BR": "Habilitar logs de rastreamento", + "pt_BR": "Habilitar Logs de Rastreamento", "ru_RU": "Включить журнал трассировки", "sv_SE": "Aktivera spårloggar", "th_TH": "เปิดใช้งานการบันทึกประวัติการติดตาม", @@ -5987,7 +6087,7 @@ "ko_KR": "방문 기록 활성화", "no_NO": "Aktiver gjestelogger", "pl_PL": "Włącz Logi Gości", - "pt_BR": "Habilitar logs do programa convidado", + "pt_BR": "Habilitar Logs de Convidados", "ru_RU": "Включить гостевые журналы", "sv_SE": "Aktivera gästloggar", "th_TH": "เปิดใช้งานการบันทึกประวัติผู้เยี่ยมชม", @@ -6012,12 +6112,12 @@ "ko_KR": "", "no_NO": "Aktivere UI-logger", "pl_PL": "", - "pt_BR": "Habilitar logs da IU", - "ru_RU": "", - "sv_SE": "", + "pt_BR": "Habilitar Logs da IU", + "ru_RU": "Включить журнал интерфейса", + "sv_SE": "Aktivera gränssnittsloggar", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Увімкнути журнали інтерфейсу", "zh_CN": "启用 UI 日志", "zh_TW": "" } @@ -6037,12 +6137,12 @@ "ko_KR": "파일 시스템 접속 기록 활성화", "no_NO": "Aktiver Fs tilgangslogger", "pl_PL": "Włącz Logi Dostępu do Systemu Plików", - "pt_BR": "Habilitar logs de acesso ao sistema de arquivos", + "pt_BR": "Habilitar Logs de Acesso Fs", "ru_RU": "Включить журналы доступа файловой системы", "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": "啟用檔案系統存取日誌" } @@ -6062,12 +6162,12 @@ "ko_KR": "파일 시스템 전역 접속 로그 모드 :", "no_NO": "Fs Global Access-logg-modus:", "pl_PL": "Tryb globalnego dziennika zdarzeń systemu plików:", - "pt_BR": "Modo global de logs do sistema de arquivos:", + "pt_BR": "Modo de Log de Acesso Global Fs:", "ru_RU": "Режим журнала глобального доступа файловой системы:", "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": "檔案系統全域存取日誌模式:" } @@ -6087,7 +6187,7 @@ "ko_KR": "개발자 옵션", "no_NO": "Utvikleralternativer", "pl_PL": "Opcje programisty (UWAGA: wpływa na wydajność)", - "pt_BR": "Opções do desenvolvedor (AVISO: Vai reduzir a performance)", + "pt_BR": "Opções do desenvolvedor", "ru_RU": "Параметры разработчика", "sv_SE": "Utvecklarinställningar", "th_TH": "ตัวเลือกนักพัฒนา", @@ -6137,7 +6237,7 @@ "ko_KR": "그래픽 후단부 기록 레벨 :", "no_NO": "Grafikk Backend-loggnivå:", "pl_PL": "Poziom rejestrowania do dziennika zdarzeń Backendu Graficznego:", - "pt_BR": "Nível de log do backend gráfico:", + "pt_BR": "Nível de Log do Renderizador Gráfico:", "ru_RU": "Уровень журнала бэкенда графики:", "sv_SE": "Loggnivå för grafikbakände:", "th_TH": "ระดับการบันทึกประวัติ กราฟิกเบื้องหลัง:", @@ -6262,7 +6362,7 @@ "ko_KR": "디버그 기록 활성화", "no_NO": "Aktiver feilsøkingslogger", "pl_PL": "Włącz dzienniki zdarzeń do debugowania", - "pt_BR": "Habilitar logs de depuração", + "pt_BR": "Habilitar Logs de Depuração", "ru_RU": "Включить журнал отладки", "sv_SE": "Aktivera felsökningsloggar", "th_TH": "เปิดใช้งาน ประวัติข้อบกพร่อง", @@ -6292,7 +6392,7 @@ "sv_SE": "Inmatning", "th_TH": "ป้อนข้อมูล", "tr_TR": "Giriş Yöntemi", - "uk_UA": "Введення", + "uk_UA": "Керування", "zh_CN": "输入", "zh_TW": "輸入" } @@ -6312,7 +6412,7 @@ "ko_KR": "도킹 모드", "no_NO": "Forankret modus", "pl_PL": "Tryb zadokowany", - "pt_BR": "Habilitar modo TV", + "pt_BR": "Modo TV", "ru_RU": "Стационарный режим", "sv_SE": "Dockat läge", "th_TH": "ด็อกโหมด", @@ -6337,7 +6437,7 @@ "ko_KR": "키보드 직접 접속", "no_NO": "Direkte tastaturtilgang", "pl_PL": "Bezpośredni dostęp do klawiatury", - "pt_BR": "Acesso direto ao teclado", + "pt_BR": "Acesso Direto ao Teclado", "ru_RU": "Прямой ввод клавиатуры", "sv_SE": "Direkt tangentbordsåtkomst", "th_TH": "เข้าถึงคีย์บอร์ดโดยตรง", @@ -6412,12 +6512,12 @@ "ko_KR": "", "no_NO": "Tilbakestill innstillinger", "pl_PL": "", - "pt_BR": "Redefinir configurações", - "ru_RU": "", - "sv_SE": "", + "pt_BR": "Redefinir Configurações", + "ru_RU": "Сбросить настройки", + "sv_SE": "Nollställ inställningar", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Скинути налаштування", "zh_CN": "重置设置", "zh_TW": "" } @@ -6438,11 +6538,11 @@ "no_NO": "Jeg vil tilbakestille innstillingene mine.", "pl_PL": "", "pt_BR": "Quero redefinir minhas configurações.", - "ru_RU": "", - "sv_SE": "", + "ru_RU": "Я хочу сбросить свои настройки.", + "sv_SE": "Jag vill nollställa mina inställningar.", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Я хочу скинути налаштування.", "zh_CN": "我要重置我的设置。", "zh_TW": "" } @@ -6463,7 +6563,7 @@ "no_NO": "", "pl_PL": "", "pt_BR": "", - "ru_RU": "", + "ru_RU": "Ок", "sv_SE": "Ok", "th_TH": "ตกลง", "tr_TR": "Tamam", @@ -6787,7 +6887,7 @@ "ko_KR": "입력 장치", "no_NO": "Inndataenhet", "pl_PL": "Urządzenie wejściowe", - "pt_BR": "Dispositivo de entrada", + "pt_BR": "Dispositivo de Entrada", "ru_RU": "Устройство ввода", "sv_SE": "Inmatningsenhet", "th_TH": "อุปกรณ์ป้อนข้อมูล", @@ -6862,7 +6962,7 @@ "ko_KR": "컨트롤러 유형", "no_NO": "Kontrollertype", "pl_PL": "Typ kontrolera", - "pt_BR": "Tipo do controle", + "pt_BR": "Tipo de Controle", "ru_RU": "Тип контроллера", "sv_SE": "Kontrollertyp", "th_TH": "ประเภทคอนโทรลเลอร์", @@ -6913,7 +7013,7 @@ "no_NO": "", "pl_PL": "Pro Kontroler", "pt_BR": "", - "ru_RU": "", + "ru_RU": "Pro контроллер", "sv_SE": "", "th_TH": "โปรคอนโทรลเลอร์", "tr_TR": "Profesyonel Kumanda", @@ -6962,7 +7062,7 @@ "ko_KR": "좌측 조이콘", "no_NO": "JoyCon venstre", "pl_PL": "Lewy JoyCon", - "pt_BR": "JoyCon esquerdo", + "pt_BR": "JoyCon Esquerdo", "ru_RU": "JoyCon (левый)", "sv_SE": "JoyCon vänster", "th_TH": "จอยคอน ด้านซ้าย", @@ -6987,7 +7087,7 @@ "ko_KR": "우측 조이콘", "no_NO": "JoyCon høyre", "pl_PL": "Prawy JoyCon", - "pt_BR": "JoyCon direito", + "pt_BR": "JoyCon Direito", "ru_RU": "JoyCon (правый)", "sv_SE": "JoyCon höger", "th_TH": "จอยคอน ด้านขวา", @@ -7312,7 +7412,7 @@ "ko_KR": "방향키", "no_NO": "Retningsfelt", "pl_PL": "Krzyżak (D-Pad)", - "pt_BR": "Direcional", + "pt_BR": "Direcional (D-Pad)", "ru_RU": "Кнопки направления (D-pad)", "sv_SE": "Riktningsknappar", "th_TH": "ปุ่มลูกศร", @@ -7587,7 +7687,7 @@ "ko_KR": "스틱 X축 반전", "no_NO": "Inverter Styrespak X", "pl_PL": "Odwróć gałkę X", - "pt_BR": "Inverter eixo X", + "pt_BR": "Inverter Eixo X", "ru_RU": "Инвертировать ось X", "sv_SE": "Invertera Spak X", "th_TH": "กลับทิศทางของแกน X", @@ -7612,7 +7712,7 @@ "ko_KR": "스틱 Y축 반전", "no_NO": "Inverter Styrespak Y", "pl_PL": "Odwróć gałkę Y", - "pt_BR": "Inverter eixo Y", + "pt_BR": "Inverter Eixo Y", "ru_RU": "Инвертировать ось Y", "sv_SE": "Invertera Spak Y", "th_TH": "กลับทิศทางของแกน Y", @@ -7637,7 +7737,7 @@ "ko_KR": "데드존 :", "no_NO": "Død sone:", "pl_PL": "Martwa strefa:", - "pt_BR": "Zona morta:", + "pt_BR": "Zona Morta:", "ru_RU": "Мёртвая зона:", "sv_SE": "Dödläge:", "th_TH": "โซนที่ไม่ทำงานของ จอยสติ๊ก:", @@ -7662,7 +7762,7 @@ "ko_KR": "좌측 스틱", "no_NO": "Venstre styrespak", "pl_PL": "Lewa Gałka", - "pt_BR": "Analógico esquerdo", + "pt_BR": "Analógico Esquerdo", "ru_RU": "Левый стик", "sv_SE": "Vänster spak", "th_TH": "จอยสติ๊ก ด้านซ้าย", @@ -7687,7 +7787,7 @@ "ko_KR": "우측 스틱", "no_NO": "Høyre styrespak", "pl_PL": "Prawa Gałka", - "pt_BR": "Analógico direito", + "pt_BR": "Analógico Direito", "ru_RU": "Правый стик", "sv_SE": "Höger spak", "th_TH": "จอยสติ๊ก ด้านขวา", @@ -7712,7 +7812,7 @@ "ko_KR": "좌측 트리거", "no_NO": "Utløsere venstre", "pl_PL": "Lewe Triggery", - "pt_BR": "Gatilhos esquerda", + "pt_BR": "Gatilho Esquerda", "ru_RU": "Триггеры слева", "sv_SE": "Avtryckare vänster", "th_TH": "ทริกเกอร์ ด้านซ้าย", @@ -7737,7 +7837,7 @@ "ko_KR": "우측 트리거", "no_NO": "Utløsere høyre", "pl_PL": "Prawe Triggery", - "pt_BR": "Gatilhos direita", + "pt_BR": "Gatilho Direita", "ru_RU": "Триггеры справа", "sv_SE": "Avtryckare höger", "th_TH": "ทริกเกอร์ ด้านขวา", @@ -7762,7 +7862,7 @@ "ko_KR": "좌측 트리거 버튼", "no_NO": "Utløserknapper Venstre", "pl_PL": "Lewe Przyciski Triggerów", - "pt_BR": "Botões de gatilho esquerda", + "pt_BR": "Botões de Gatilho da Esquerda", "ru_RU": "Триггерные кнопки слева", "sv_SE": "Avtryckare knappar vänster", "th_TH": "ปุ่มทริกเกอร์ ด้านซ้าย", @@ -7787,7 +7887,7 @@ "ko_KR": "우측 트리거 버튼", "no_NO": "Utløserknapper høyre", "pl_PL": "Prawe Przyciski Triggerów", - "pt_BR": "Botões de gatilho direita", + "pt_BR": "Botões de Gatilho da Direita", "ru_RU": "Триггерные кнопки справа", "sv_SE": "Avtryckare knappar höger", "th_TH": "ปุ่มทริกเกอร์ ด้านขวา", @@ -8037,7 +8137,7 @@ "ko_KR": "좌측 버튼", "no_NO": "Knapper til venstre", "pl_PL": "Lewe Przyciski", - "pt_BR": "Botões esquerda", + "pt_BR": "Botões da Esquerda", "ru_RU": "Левые кнопки", "sv_SE": "Knappar vänster", "th_TH": "ปุ่มกดเสริม ด้านซ้าย", @@ -8062,7 +8162,7 @@ "ko_KR": "우측 버튼", "no_NO": "Knapper til høyre", "pl_PL": "Prawe Przyciski", - "pt_BR": "Botões direita", + "pt_BR": "Botões da Direita", "ru_RU": "Правые кнопки", "sv_SE": "Knappar höger", "th_TH": "ปุ่มกดเสริม ด้านขวา", @@ -8112,7 +8212,7 @@ "ko_KR": "트리거 임계값 :", "no_NO": "Utløser terskel:", "pl_PL": "Próg Triggerów:", - "pt_BR": "Sensibilidade do gatilho:", + "pt_BR": "Sensibilidade do Gatilho:", "ru_RU": "Порог срабатывания:", "sv_SE": "Tröskelvärde avtryckare:", "th_TH": "ตั้งค่าขีดจำกัดการกด:", @@ -8137,7 +8237,7 @@ "ko_KR": "모션", "no_NO": "Bevegelse", "pl_PL": "Ruch", - "pt_BR": "Sensor de movimento", + "pt_BR": "Sensor de Movimento", "ru_RU": "Движение", "sv_SE": "Rörelse", "th_TH": "การเคลื่อนไหว", @@ -8162,7 +8262,7 @@ "ko_KR": "CemuHook 호환 모션 사용", "no_NO": "Bruk CemuHook kompatibel bevegelse", "pl_PL": "Użyj ruchu zgodnego z CemuHook", - "pt_BR": "Usar sensor compatível com CemuHook", + "pt_BR": "Usar Sensor Compatível com CemuHook", "ru_RU": "Включить совместимость с CemuHook", "sv_SE": "Använd CemuHook-kompatibel rörelse", "th_TH": "ใช้การเคลื่อนไหวที่เข้ากันได้กับ CemuHook", @@ -8187,7 +8287,7 @@ "ko_KR": "컨트롤러 슬롯 :", "no_NO": "Kontrollertype:", "pl_PL": "Slot Kontrolera:", - "pt_BR": "Slot do controle:", + "pt_BR": "Slot do Controle:", "ru_RU": "Слот контроллера:", "sv_SE": "Kontrollerplats:", "th_TH": "ช่องเสียบ คอนโทรลเลอร์:", @@ -8212,7 +8312,7 @@ "ko_KR": "미러 입력", "no_NO": "Speilvend", "pl_PL": "Odzwierciedlaj Sterowanie", - "pt_BR": "Espelhar movimento", + "pt_BR": "Espelhar Movimento", "ru_RU": "Зеркальный ввод", "sv_SE": "Spegla inmatning", "th_TH": "นำเข้าการสะท้อน การควบคุม", @@ -8237,7 +8337,7 @@ "ko_KR": "우측 조이콘 슬롯:", "no_NO": "Høyre JoyCon set:", "pl_PL": "Prawy Slot JoyCon:", - "pt_BR": "Slot do JoyCon direito:", + "pt_BR": "Slot do JoyCon Direito:", "ru_RU": "Слот правого JoyCon:", "sv_SE": "Höger JoyCon-plats:", "th_TH": "ช่องเสียบ จอยคอน ด้านขวา:", @@ -8262,7 +8362,7 @@ "ko_KR": "서버 호스트 :", "no_NO": "Server Vert:", "pl_PL": "Host Serwera:", - "pt_BR": "Endereço do servidor:", + "pt_BR": "Endereço do Servidor:", "ru_RU": "Хост сервера:", "sv_SE": "Servervärd:", "th_TH": "เจ้าของเซิร์ฟเวอร์:", @@ -8287,7 +8387,7 @@ "ko_KR": "자이로 감도 :", "no_NO": "Gyro følsomhet:", "pl_PL": "Czułość Żyroskopu:", - "pt_BR": "Sensibilidade do giroscópio:", + "pt_BR": "Sensibilidade do Giroscópio:", "ru_RU": "Чувствительность гироскопа:", "sv_SE": "Känslighet för gyro:", "th_TH": "ความไวของ Gyro:", @@ -8312,7 +8412,7 @@ "ko_KR": "자이로 데드존 :", "no_NO": "Gyro Dødsone:", "pl_PL": "Deadzone Żyroskopu:", - "pt_BR": "Zona morta do giroscópio:", + "pt_BR": "Zona Morta do Giroscópio:", "ru_RU": "Мертвая зона гироскопа:", "sv_SE": "Dödläge för gyro:", "th_TH": "ส่วนไม่ทำงานของ Gyro:", @@ -8338,7 +8438,7 @@ "no_NO": "", "pl_PL": "", "pt_BR": "", - "ru_RU": "", + "ru_RU": "LED-подсветка", "sv_SE": "", "th_TH": "", "tr_TR": "", @@ -8362,8 +8462,8 @@ "ko_KR": "비활성화", "no_NO": "Deaktiver", "pl_PL": "", - "pt_BR": "", - "ru_RU": "", + "pt_BR": "Desabilitar", + "ru_RU": "Отключить", "sv_SE": "Inaktivera", "th_TH": "", "tr_TR": "", @@ -8387,8 +8487,8 @@ "ko_KR": "레인보우", "no_NO": "Regnbue", "pl_PL": "", - "pt_BR": "", - "ru_RU": "", + "pt_BR": "Arco-íris", + "ru_RU": "Радужная", "sv_SE": "Regnbåge", "th_TH": "", "tr_TR": "", @@ -8412,9 +8512,9 @@ "ko_KR": "레인보우 속도", "no_NO": "Regnbue Hastighet", "pl_PL": "", - "pt_BR": "", - "ru_RU": "", - "sv_SE": "", + "pt_BR": "Velocidade do Arco-íris", + "ru_RU": "Скорость переливания", + "sv_SE": "Regnbågshastighet", "th_TH": "", "tr_TR": "", "uk_UA": "", @@ -8437,8 +8537,8 @@ "ko_KR": "색상", "no_NO": "Farge", "pl_PL": "", - "pt_BR": "", - "ru_RU": "", + "pt_BR": "Cor", + "ru_RU": "Цвет", "sv_SE": "Färg", "th_TH": "", "tr_TR": "", @@ -8537,7 +8637,7 @@ "ko_KR": "좌측 Shift", "no_NO": "Skift venstre", "pl_PL": "", - "pt_BR": "", + "pt_BR": "Shift Esquerdo", "ru_RU": "Левый Shift", "sv_SE": "Skift vänster", "th_TH": "", @@ -8562,7 +8662,7 @@ "ko_KR": "우측 Shift", "no_NO": "Skift høyre", "pl_PL": "", - "pt_BR": "", + "pt_BR": "Shift Direito", "ru_RU": "Правый Shift", "sv_SE": "Skift höger", "th_TH": "", @@ -8587,7 +8687,7 @@ "ko_KR": "좌측 Ctrl", "no_NO": "Ctrl venstre", "pl_PL": "", - "pt_BR": "", + "pt_BR": "Ctrl Esquerdo", "ru_RU": "Левый Ctrl", "sv_SE": "Ctrl vänster", "th_TH": "", @@ -8612,7 +8712,7 @@ "ko_KR": "좌측 ⌃", "no_NO": "⌃ Venstre", "pl_PL": "", - "pt_BR": "", + "pt_BR": "⌃ Esquerda", "ru_RU": "Левый ⌃", "sv_SE": "^ Vänster", "th_TH": "", @@ -8637,7 +8737,7 @@ "ko_KR": "우측 Ctrl", "no_NO": "Ctrl høyre", "pl_PL": "", - "pt_BR": "", + "pt_BR": "Ctrl Direito", "ru_RU": "Правый Ctrl", "sv_SE": "Ctrl höger", "th_TH": "", @@ -8662,7 +8762,7 @@ "ko_KR": "우측 ⌃", "no_NO": "⌃ Høyre", "pl_PL": "", - "pt_BR": "", + "pt_BR": "⌃ Direito", "ru_RU": "Правый ⌃", "sv_SE": "^ Höger", "th_TH": "", @@ -8687,7 +8787,7 @@ "ko_KR": "좌측 Alt", "no_NO": "Alt Venstre", "pl_PL": "", - "pt_BR": "", + "pt_BR": "Alt Esquerdo", "ru_RU": "Левый Alt", "sv_SE": "Alt vänster", "th_TH": "", @@ -8712,7 +8812,7 @@ "ko_KR": "좌측 ⌥", "no_NO": "⌥ Venstre", "pl_PL": "", - "pt_BR": "", + "pt_BR": "⌥ Esquerda", "ru_RU": "Левый ⌥", "sv_SE": "⌥ vänster", "th_TH": "", @@ -8737,7 +8837,7 @@ "ko_KR": "우측 Alt", "no_NO": "Alt høyre", "pl_PL": "", - "pt_BR": "", + "pt_BR": "Alt Direito", "ru_RU": "Правый Alt", "sv_SE": "Alt höger", "th_TH": "", @@ -8762,7 +8862,7 @@ "ko_KR": "우측 ⌥", "no_NO": "⌥ Høyre", "pl_PL": "", - "pt_BR": "", + "pt_BR": "⌥ Direito", "ru_RU": "Правый ⌥", "sv_SE": "⌥ höger", "th_TH": "", @@ -8787,7 +8887,7 @@ "ko_KR": "좌측 ⊞", "no_NO": "⊞ Venstre", "pl_PL": "", - "pt_BR": "", + "pt_BR": "⊞ Esquerdo", "ru_RU": "Левый ⊞", "sv_SE": "⊞ vänster", "th_TH": "", @@ -8812,7 +8912,7 @@ "ko_KR": "좌측 ⌘", "no_NO": "⌘ Venstre", "pl_PL": "", - "pt_BR": "", + "pt_BR": "⌘ Esquerdo", "ru_RU": "Левый ⌘", "sv_SE": "⌘ vänster", "th_TH": "", @@ -8837,7 +8937,7 @@ "ko_KR": "우측 ⊞", "no_NO": "⊞ Høyre", "pl_PL": "", - "pt_BR": "", + "pt_BR": "⊞ Direito", "ru_RU": "Правый ⊞", "sv_SE": "⊞ höger", "th_TH": "", @@ -8862,7 +8962,7 @@ "ko_KR": "우측 ⌘", "no_NO": "⌘ Høyre", "pl_PL": "", - "pt_BR": "", + "pt_BR": "⌘ Direito", "ru_RU": "Правый ⌘", "sv_SE": "⌘ höger", "th_TH": "", @@ -8912,7 +9012,7 @@ "ko_KR": "↑", "no_NO": "Opp", "pl_PL": "", - "pt_BR": "", + "pt_BR": "Cima", "ru_RU": "Вверх", "sv_SE": "Upp", "th_TH": "", @@ -8937,7 +9037,7 @@ "ko_KR": "↓", "no_NO": "Ned", "pl_PL": "", - "pt_BR": "", + "pt_BR": "Baixo", "ru_RU": "Вниз", "sv_SE": "Ner", "th_TH": "", @@ -8962,7 +9062,7 @@ "ko_KR": "←", "no_NO": "Venstre", "pl_PL": "", - "pt_BR": "", + "pt_BR": "Esquerda", "ru_RU": "Влево", "sv_SE": "Vänster", "th_TH": "", @@ -8987,7 +9087,7 @@ "ko_KR": "→", "no_NO": "Høyre", "pl_PL": "", - "pt_BR": "", + "pt_BR": "Direita", "ru_RU": "Вправо", "sv_SE": "Höger", "th_TH": "", @@ -9037,7 +9137,7 @@ "ko_KR": "Esc", "no_NO": "Esc-tast", "pl_PL": "", - "pt_BR": "", + "pt_BR": "Esc", "ru_RU": "Esc", "sv_SE": "", "th_TH": "", @@ -9062,7 +9162,7 @@ "ko_KR": "스페이스", "no_NO": "Mellomrom", "pl_PL": "", - "pt_BR": "", + "pt_BR": "Espaço", "ru_RU": "Пробел", "sv_SE": "Blanksteg", "th_TH": "", @@ -9463,7 +9563,7 @@ "no_NO": "Numerisk 1", "pl_PL": "", "pt_BR": "", - "ru_RU": "1 (цифровий блок)", + "ru_RU": "Блок цифр 1", "sv_SE": "", "th_TH": "", "tr_TR": "", @@ -10387,7 +10487,7 @@ "ko_KR": "연동 해제", "no_NO": "Ikke bundet", "pl_PL": "", - "pt_BR": "", + "pt_BR": "Não Atribuído", "ru_RU": "Не привязано", "sv_SE": "Obunden", "th_TH": "", @@ -10717,7 +10817,7 @@ "sv_SE": "", "th_TH": "", "tr_TR": "Rehber", - "uk_UA": "", + "uk_UA": "Меню", "zh_CN": "主页键", "zh_TW": "快顯功能表鍵" } @@ -10867,7 +10967,7 @@ "sv_SE": "", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Сенсорна панель", "zh_CN": "触摸板", "zh_TW": "觸控板" } @@ -11037,7 +11137,7 @@ "ko_KR": "선택된 사용자 프로필 :", "no_NO": "Valgt brukerprofil:", "pl_PL": "Wybrany profil użytkownika:", - "pt_BR": "Perfil de usuário selecionado:", + "pt_BR": "Perfil de Usuário Selecionado:", "ru_RU": "Выбранный пользовательский профиль:", "sv_SE": "Vald användarprofil:", "th_TH": "เลือกโปรไฟล์ผู้ใช้งาน:", @@ -11062,7 +11162,7 @@ "ko_KR": "프로필 이름 저장", "no_NO": "Lagre profilnavnet", "pl_PL": "Zapisz nazwę profilu", - "pt_BR": "Salvar nome de perfil", + "pt_BR": "Salvar Nome de Perfil", "ru_RU": "Сохранить пользовательский профиль", "sv_SE": "Spara profilnamn", "th_TH": "บันทึกชื่อโปรไฟล์", @@ -11087,7 +11187,7 @@ "ko_KR": "프로필 이미지 변경", "no_NO": "Endre profilbilde", "pl_PL": "Zmień obrazek profilu", - "pt_BR": "Mudar imagem de perfil", + "pt_BR": "Mudar Imagem de Perfil", "ru_RU": "Изменить аватар", "sv_SE": "Byt profilbild", "th_TH": "เปลี่ยนรูปโปรไฟล์", @@ -11112,7 +11212,7 @@ "ko_KR": "사용 가능한 사용자 프로필 :", "no_NO": "Tilgjengelige brukerprofiler:", "pl_PL": "Dostępne profile użytkownika:", - "pt_BR": "Perfis de usuário disponíveis:", + "pt_BR": "Perfis de Usuário Disponíveis:", "ru_RU": "Доступные профили пользователей:", "sv_SE": "Tillgängliga användarprofiler:", "th_TH": "โปรไฟล์ผู้ใช้ที่ใช้งานได้:", @@ -11137,7 +11237,7 @@ "ko_KR": "프로필 만들기", "no_NO": "Opprett Profil", "pl_PL": "Utwórz profil", - "pt_BR": "Adicionar novo perfil", + "pt_BR": "Criar Perfil", "ru_RU": "Добавить новый профиль", "sv_SE": "Skapa profil", "th_TH": "สร้างโปรไฟล์ใหม่", @@ -11212,7 +11312,7 @@ "ko_KR": "별명 선택", "no_NO": "Velg ett kallenavn", "pl_PL": "Wybierz pseudonim", - "pt_BR": "Escolha um apelido", + "pt_BR": "Escolha um Apelido", "ru_RU": "Введите никнейм", "sv_SE": "Välj ett smeknamn", "th_TH": "เลือก ชื่อเล่น", @@ -11237,7 +11337,7 @@ "ko_KR": "프로필 이미지 선택", "no_NO": "Valg av profilbilde", "pl_PL": "Wybór Obrazu Profilu", - "pt_BR": "Seleção da imagem de perfil", + "pt_BR": "Seleção da Imagem de Perfil", "ru_RU": "Выбор изображения профиля", "sv_SE": "Välj profilbild", "th_TH": "เลือก รูปโปรไฟล์ ของคุณ", @@ -11262,7 +11362,7 @@ "ko_KR": "프로필 이미지를 선택", "no_NO": "Velg et profilbilde", "pl_PL": "Wybierz zdjęcie profilowe", - "pt_BR": "Escolha uma imagem de perfil", + "pt_BR": "Escolha uma Imagem de Perfil", "ru_RU": "Выбор аватара", "sv_SE": "Välj en profilbild", "th_TH": "เลือก รูปโปรไฟล์", @@ -11312,7 +11412,7 @@ "ko_KR": "이미지 파일 가져오기", "no_NO": "Importer bildefil", "pl_PL": "Importuj Plik Obrazu", - "pt_BR": "Importar arquivo de imagem", + "pt_BR": "Importar Arquivo de Imagem", "ru_RU": "Импорт изображения", "sv_SE": "Importera bildfil", "th_TH": "นำเข้า ไฟล์รูปภาพ", @@ -11337,7 +11437,7 @@ "ko_KR": "펌웨어 아바타 선택", "no_NO": "Velg fastvare profilbilde", "pl_PL": "Wybierz domyślny awatar z oprogramowania konsoli", - "pt_BR": "Selecionar avatar do firmware", + "pt_BR": "Selecionar Avatar do Firmware", "ru_RU": "Встроенные аватары", "sv_SE": "Välj avatar från firmware", "th_TH": "เลือก รูปอวาต้า จากระบบ", @@ -11437,7 +11537,7 @@ "ko_KR": "취소하기", "no_NO": "Kansellerer", "pl_PL": "", - "pt_BR": "", + "pt_BR": "Cancelando", "ru_RU": "Отмена", "sv_SE": "Avbryter", "th_TH": "", @@ -11487,7 +11587,7 @@ "ko_KR": "프로필 이름 선택", "no_NO": "Velg profilnavnet", "pl_PL": "Wybierz nazwę profilu", - "pt_BR": "Escolha o nome de perfil", + "pt_BR": "Escolha o Nome de Perfil", "ru_RU": "Выберите никнейм", "sv_SE": "Välj ett profilnamn", "th_TH": "เลือก ชื่อโปรไฟล์", @@ -11512,7 +11612,7 @@ "ko_KR": "프로필 이름을 입력", "no_NO": "Vennligst skriv inn et profilnavn", "pl_PL": "Wprowadź nazwę profilu", - "pt_BR": "Escreva o nome do perfil", + "pt_BR": "Escreva o Nome do Perfil", "ru_RU": "Пожалуйста, введите никнейм", "sv_SE": "Ange ett profilnamn", "th_TH": "กรุณาใส่ชื่อโปรไฟล์", @@ -11537,7 +11637,7 @@ "ko_KR": "(최대 길이 : {0})", "no_NO": "(Maks lengde: {0})", "pl_PL": "(Maksymalna długość: {0})", - "pt_BR": "(Máximo de caracteres: {0})", + "pt_BR": "(Máximo de Caracteres: {0})", "ru_RU": "(Максимальная длина: {0})", "sv_SE": "(Max längd: {0})", "th_TH": "(ความยาวสูงสุด: {0})", @@ -11562,7 +11662,7 @@ "ko_KR": "아바타 선택", "no_NO": "Velg profilbilde", "pl_PL": "Wybierz awatar", - "pt_BR": "Escolher", + "pt_BR": "Escolha o Avatar", "ru_RU": "Выбрать аватар", "sv_SE": "Välj avatar", "th_TH": "เลือก รูปอวาต้า ของคุณ", @@ -11587,7 +11687,7 @@ "ko_KR": "배경색 설정", "no_NO": "Angi bakgrunnsfarge", "pl_PL": "Ustaw kolor tła", - "pt_BR": "Definir cor de fundo", + "pt_BR": "Definir cor de Fundo", "ru_RU": "Установить цвет фона", "sv_SE": "Välj bakgrundsfärg", "th_TH": "ตั้งค่าสีพื้นหลัง", @@ -11637,7 +11737,7 @@ "ko_KR": "프로필 불러오기", "no_NO": "Last inn profil", "pl_PL": "Wczytaj profil", - "pt_BR": "Carregar perfil", + "pt_BR": "Carregar Perfil", "ru_RU": "Загрузить профиль", "sv_SE": "Läs in profil", "th_TH": "โหลด โปรไฟล์", @@ -11662,7 +11762,7 @@ "ko_KR": "프로필 보기", "no_NO": "Se Profil", "pl_PL": "", - "pt_BR": "Ver perfil", + "pt_BR": "Ver Perfil", "ru_RU": "Показать профиль", "sv_SE": "Visa profil", "th_TH": "", @@ -11687,7 +11787,7 @@ "ko_KR": "프로필 추가", "no_NO": "Legg til profil", "pl_PL": "Dodaj profil", - "pt_BR": "Adicionar perfil", + "pt_BR": "Adicionar Perfil", "ru_RU": "Добавить профиль", "sv_SE": "Lägg till profil", "th_TH": "เพิ่ม โปรไฟล์", @@ -11712,7 +11812,7 @@ "ko_KR": "프로필 삭제", "no_NO": "Fjern profil", "pl_PL": "Usuń profil", - "pt_BR": "Remover perfil", + "pt_BR": "Remover Perfil", "ru_RU": "Удалить профиль", "sv_SE": "Ta bort profil", "th_TH": "ลบ โปรไฟล์", @@ -11737,7 +11837,7 @@ "ko_KR": "프로필 추가", "no_NO": "Lagre Profil", "pl_PL": "Zapisz profil", - "pt_BR": "Salvar perfil", + "pt_BR": "Salvar Perfil", "ru_RU": "Сохранить профиль", "sv_SE": "Spara profil", "th_TH": "บันทึก โปรไฟล์", @@ -11762,7 +11862,7 @@ "ko_KR": "스크린샷 찍기", "no_NO": "Ta skjermbilde", "pl_PL": "Zrób zrzut ekranu", - "pt_BR": "Salvar captura de tela", + "pt_BR": "Tirar Captura de tela", "ru_RU": "Сделать снимок экрана", "sv_SE": "Ta skärmbild", "th_TH": "ถ่ายภาพหน้าจอ", @@ -11837,7 +11937,7 @@ "ko_KR": "즐겨찾기 전환", "no_NO": "Vis/Skjul favoritter", "pl_PL": "Przełącz na ulubione", - "pt_BR": "Alternar favorito", + "pt_BR": "Marcar como Favorito", "ru_RU": "Добавить в избранное", "sv_SE": "Växla som favorit", "th_TH": "สลับรายการโปรด", @@ -11862,8 +11962,8 @@ "ko_KR": "게임의 즐겨찾기 상태 전환", "no_NO": "Vis/Skjul favorittstatus for spillet", "pl_PL": "Przełącz status Ulubionej Gry", - "pt_BR": "Marca ou desmarca jogo como favorito", - "ru_RU": "Добавляет игру в избранное и помечает звездочкой", + "pt_BR": "Marca ou desmarca o jogo como favorito", + "ru_RU": "Добавляет игру в избранное и помечает звёздочкой", "sv_SE": "Växla favoritstatus för spelet", "th_TH": "สลับสถานะเกมที่ชื่นชอบ", "tr_TR": "Oyunu Favorilere Ekle/Çıkar", @@ -11938,7 +12038,7 @@ "no_NO": "Mørk", "pl_PL": "Ciemny", "pt_BR": "Escuro", - "ru_RU": "Темная", + "ru_RU": "Тёмная", "sv_SE": "Mörkt", "th_TH": "มืด", "tr_TR": "Karanlık", @@ -12037,7 +12137,7 @@ "ko_KR": "강력한 진동 증폭기", "no_NO": "Sterk Vibrasjon multiplikator", "pl_PL": "Mnożnik mocnych wibracji", - "pt_BR": "Multiplicador de vibração forte", + "pt_BR": "Multiplicador de Vibração Forte", "ru_RU": "Множитель сильной вибрации", "sv_SE": "Försvaga stark vibration", "th_TH": "เพิ่มความแรงการสั่น", @@ -12062,7 +12162,7 @@ "ko_KR": "약한 진동 증폭기", "no_NO": "Svak Vibrasjon multiplikator", "pl_PL": "Mnożnik słabych wibracji", - "pt_BR": "Multiplicador de vibração fraca", + "pt_BR": "Multiplicador de Vibração Fraca", "ru_RU": "Множитель слабой вибрации", "sv_SE": "Förstärk svag vibration", "th_TH": "ลดความแรงการสั่น", @@ -12112,7 +12212,7 @@ "ko_KR": "이 게임의 저장 데이터를 만들겠습니까?", "no_NO": "Vil du lage lagrede data for dette spillet", "pl_PL": "Czy chcesz utworzyć zapis danych dla tej gry?", - "pt_BR": "Gostaria de criar o diretório de salvamento para esse jogo?", + "pt_BR": "Você gostaria de criar dados salvos para este jogo?", "ru_RU": "Создать сохранение для этой игры?", "sv_SE": "Vill du skapa sparat spel för detta spel?", "th_TH": "คุณต้องการสร้างบันทึกข้อมูลสำหรับเกมนี้หรือไม่?", @@ -12138,7 +12238,7 @@ "no_NO": "{0} - Bekreftelse", "pl_PL": "{0} - Potwierdzenie", "pt_BR": "{0} - Confirmação", - "ru_RU": "{0} - Подтверждение", + "ru_RU": "{0} — Подтверждение", "sv_SE": "{0} - Bekräftelse", "th_TH": "{0} - ยืนยัน", "tr_TR": "{0} - Onay", @@ -12163,7 +12263,7 @@ "no_NO": "{0} – Oppdaterer", "pl_PL": "{0} - Asystent aktualizacji", "pt_BR": "{0} - Atualizador", - "ru_RU": "{0} - Обновление", + "ru_RU": "{0} — Обновление", "sv_SE": "{0} - Uppdatering", "th_TH": "{0} - อัพเดต", "tr_TR": "{0} - Güncelleyici", @@ -12188,7 +12288,7 @@ "no_NO": "{0} - Feil", "pl_PL": "{0} - Błąd", "pt_BR": "{0} - Erro", - "ru_RU": "{0} - Ошибка", + "ru_RU": "{0} — Ошибка", "sv_SE": "{0} - Fel", "th_TH": "{0} - ผิดพลาด", "tr_TR": "{0} - Hata", @@ -12213,7 +12313,7 @@ "no_NO": "{0} - Advarsel", "pl_PL": "{0} - Ostrzeżenie", "pt_BR": "{0} - Alerta", - "ru_RU": "{0} - Предупреждение", + "ru_RU": "{0} — Предупреждение", "sv_SE": "{0} - Varning", "th_TH": "{0} - คำเตือน", "tr_TR": "{0} - Uyarı", @@ -12238,7 +12338,7 @@ "no_NO": "{0} - Avslutt", "pl_PL": "{0} - Wyjdź", "pt_BR": "{0} - Sair", - "ru_RU": "{0} - Выход", + "ru_RU": "{0} — Выход", "sv_SE": "{0} - Avslut", "th_TH": "{0} - ออก", "tr_TR": "{0} - Çıkış", @@ -12313,7 +12413,7 @@ "no_NO": "Alle ulagrede data vil gå tapt!", "pl_PL": "Wszystkie niezapisane dane zostaną utracone!", "pt_BR": "Todos os dados que não foram salvos serão perdidos!", - "ru_RU": "Все несохраненные данные будут потеряны", + "ru_RU": "Все несохранённые данные будут потеряны", "sv_SE": "Allt data som inte sparats kommer att förloras!", "th_TH": "ข้อมูลทั้งหมดที่ไม่ได้บันทึกทั้งหมดจะสูญหาย!", "tr_TR": "Kaydedilmeyen bütün veriler kaybedilecek!", @@ -12337,7 +12437,7 @@ "ko_KR": "지정된 저장 데이터를 생성하는 동안 오류가 발생 : {0}", "no_NO": "Det oppstod en feil under oppretting av den angitte lagredata: {0}", "pl_PL": "Wystąpił błąd podczas tworzenia określonych zapisanych danych: {0}", - "pt_BR": "Ocorreu um erro ao criar o diretório de salvamento: {0}", + "pt_BR": "Ocorreu um erro ao criar os dados salvos especificados: {0}", "ru_RU": "Произошла ошибка при создании указанных данных сохранения: {0}", "sv_SE": "Det inträffade ett fel vid skapandet av angivet sparat spel: {0}", "th_TH": "มีข้อผิดพลาดในการสร้างข้อมูลบันทึกที่ระบุ: {0}", @@ -12362,7 +12462,7 @@ "ko_KR": "지정된 저장 데이터를 찾는 중 오류가 발생 : {0}", "no_NO": "Det oppstod en feil under oppretting av den angitte lagredata: {0}", "pl_PL": "Wystąpił błąd podczas próby znalezienia określonych zapisanych danych: {0}", - "pt_BR": "Ocorreu um erro ao tentar encontrar o diretório de salvamento: {0}", + "pt_BR": "Ocorreu um erro ao encontrar os dados salvos especificados: {0}", "ru_RU": "Произошла ошибка при поиске указанных данных сохранения: {0}", "sv_SE": "Det inträffade ett fel vid sökandet av angivet sparat spel: {0}", "th_TH": "มีข้อผิดพลาดในการค้นหาข้อมูลบันทึกที่ระบุไว้: {0}", @@ -12387,7 +12487,7 @@ "ko_KR": "압축을 풀 폴더를 선택", "no_NO": "Velg mappen å pakke ut i", "pl_PL": "Wybierz folder, do którego chcesz rozpakować", - "pt_BR": "Escolha o diretório onde os arquivos serão extraídos", + "pt_BR": "Escolha a pasta para extrair", "ru_RU": "Выберите папку для извлечения", "sv_SE": "Välj en mapp att extrahera till", "th_TH": "เลือกโฟลเดอร์ที่จะแตกไฟล์เข้าไป", @@ -12437,7 +12537,7 @@ "ko_KR": "NCA 단면 추출기", "no_NO": "Ryujinx - NCA Seksjon Ekstraktor", "pl_PL": "Asystent wypakowania sekcji NCA", - "pt_BR": "Extrator de seções NCA", + "pt_BR": "Extrator de Seções NCA", "ru_RU": "Извлечение разделов NCA", "sv_SE": "Ryujinx - Extrahera NCA-sektion", "th_TH": "เครื่องมือแตกไฟล์ของ NCA", @@ -12637,7 +12737,7 @@ "ko_KR": "GitHub에서 받은 Ryujinx 버전을 변환하지 못했습니다.", "no_NO": "Kan ikke konvertere mottatt Ryujinx-versjon fra GitHub Utgivelse.", "pl_PL": "Nie udało się przekonwertować otrzymanej wersji Ryujinx z Github Release.", - "pt_BR": "Falha ao converter a versão do Ryujinx recebida do AppVeyor.", + "pt_BR": "Falha ao converter a versão Ryujinx recebida do GitHub.", "ru_RU": "Не удалось преобразовать полученную версию Ryujinx из GitHub Release.", "sv_SE": "Misslyckades med att konvertera mottagen Ryujinx-version från GitHub.", "th_TH": "ไม่สามารถแปลงเวอร์ชั่น Ryujinx ที่ได้รับจาก GitHub Release", @@ -12662,7 +12762,7 @@ "ko_KR": "업데이트 내려받는 중...", "no_NO": "Laster ned oppdatering...", "pl_PL": "Pobieranie aktualizacji...", - "pt_BR": "Baixando atualização...", + "pt_BR": "Baixando Atualização...", "ru_RU": "Загрузка обновления...", "sv_SE": "Hämtar uppdatering...", "th_TH": "กำลังดาวน์โหลดอัปเดต...", @@ -12672,6 +12772,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": { @@ -12687,7 +12812,7 @@ "ko_KR": "업데이트 추출 중...", "no_NO": "Pakker ut oppdatering...", "pl_PL": "Wypakowywanie Aktualizacji...", - "pt_BR": "Extraindo atualização...", + "pt_BR": "Extraindo Atualização...", "ru_RU": "Извлечение обновления...", "sv_SE": "Extraherar uppdatering...", "th_TH": "กำลังแตกไฟล์อัปเดต...", @@ -12712,7 +12837,7 @@ "ko_KR": "이름 변경 업데이트...", "no_NO": "Endrer navn på oppdatering...", "pl_PL": "Zmiana Nazwy Aktualizacji...", - "pt_BR": "Renomeando atualização...", + "pt_BR": "Renomeando Atualização...", "ru_RU": "Переименование обновления...", "sv_SE": "Byter namn på uppdatering...", "th_TH": "กำลังลบไฟล์เก่า...", @@ -12737,7 +12862,7 @@ "ko_KR": "새 업데이트 추가 중...", "no_NO": "Legger til ny oppdatering...", "pl_PL": "Dodawanie Nowej Aktualizacji...", - "pt_BR": "Adicionando nova atualização...", + "pt_BR": "Adicionando Nova Atualização...", "ru_RU": "Добавление нового обновления...", "sv_SE": "Lägger till ny uppdatering...", "th_TH": "กำลังเพิ่มไฟล์อัปเดตใหม่...", @@ -12762,7 +12887,7 @@ "ko_KR": "변경 로그 보기", "no_NO": "Vis endringslogg", "pl_PL": "", - "pt_BR": "", + "pt_BR": "Ver Registro de Alterações", "ru_RU": "Показать список изменений", "sv_SE": "Visa ändringslogg", "th_TH": "", @@ -12787,7 +12912,7 @@ "ko_KR": "업데이트가 완료되었습니다!", "no_NO": "Oppdateringen er fullført!", "pl_PL": "Aktualizacja Zakończona!", - "pt_BR": "Atualização concluída!", + "pt_BR": "Atualização Concluída!", "ru_RU": "Обновление завершено", "sv_SE": "Uppdatering färdig!", "th_TH": "อัปเดตเสร็จสมบูรณ์แล้ว!", @@ -12937,7 +13062,7 @@ "ko_KR": "다시 시작 필요", "no_NO": "Omstart Kreves", "pl_PL": "Wymagane Ponowne Uruchomienie", - "pt_BR": "Reinicialização necessária", + "pt_BR": "Reinicialização Necessária", "ru_RU": "Требуется перезапуск", "sv_SE": "Omstart krävs", "th_TH": "จำเป็นต้องรีสตาร์ทเพื่อให้การอัพเดตสามารถให้งานได้", @@ -13062,7 +13187,7 @@ "ko_KR": "펌웨어가 설치되어 있지 않음", "no_NO": "Ingen fastvare installert", "pl_PL": "Brak Zainstalowanego Firmware'u", - "pt_BR": "Firmware não foi instalado", + "pt_BR": "Nenhum Firmware Instalado", "ru_RU": "Прошивка не установлена", "sv_SE": "Inget firmware installerat", "th_TH": "ไม่มีการติดตั้งเฟิร์มแวร์", @@ -13212,7 +13337,7 @@ "ko_KR": "설정 창 열기", "no_NO": "Åpne innstillinger-vinduet", "pl_PL": "Otwórz Okno Ustawień", - "pt_BR": "Abrir janela de configurações", + "pt_BR": "Abrir Janela de Configurações", "ru_RU": "Открывает окно параметров", "sv_SE": "Öppna inställningar", "th_TH": "เปิดหน้าต่างการตั้งค่า", @@ -13237,7 +13362,7 @@ "ko_KR": "XCI 트리머 창", "no_NO": "XCI Trimmervindu", "pl_PL": "", - "pt_BR": "Janela de redução XCI", + "pt_BR": "Janela de Redução XCI", "ru_RU": "Окно триммера XCI", "sv_SE": "XCI-optimerare", "th_TH": "", @@ -13262,7 +13387,7 @@ "ko_KR": "컨트롤러 애플릿", "no_NO": "Kontroller Applet", "pl_PL": "Aplet Kontrolera", - "pt_BR": "Applet de controle", + "pt_BR": "Applet de Controle", "ru_RU": "Апплет контроллера", "sv_SE": "Handkontroller-applet", "th_TH": "คอนโทรลเลอร์ Applet", @@ -13442,7 +13567,7 @@ "sv_SE": "Amiibo-API", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "API Amiibo", "zh_CN": "", "zh_TW": "" } @@ -13562,7 +13687,7 @@ "ko_KR": "프로필 삭제하기", "no_NO": "Sletter profil", "pl_PL": "Usuwanie Profilu", - "pt_BR": "Apagando perfil", + "pt_BR": "Apagando Perfil", "ru_RU": "Удаление профиля", "sv_SE": "Tar bort profilen", "th_TH": "กำลังลบโปรไฟล์", @@ -13637,7 +13762,7 @@ "ko_KR": "다음에 부팅할 때, PPTC 재구축을 대기열에 추가하려고 합니다.\n\n{0}\n\n계속하시겠습니까?", "no_NO": "Du er i ferd med å bygge en PPTC i køen ved neste oppstart av:\n\n{0}\n\nEr du sikker på at du vil fortsette?", "pl_PL": "Masz zamiar umieścić w kolejce rekompilację PPTC przy następnym uruchomieniu:\n\n{0}\n\nCzy na pewno chcesz kontynuować?", - "pt_BR": "Você está prestes a apagar o cache PPTC para :\n\n{0}\n\nTem certeza que deseja continuar?", + "pt_BR": "Você está prestes a enfileirar uma reconstrução PPTC na próxima inicialização de:\n\n{0}\n\nTem certeza de que deseja continuar?", "ru_RU": "Вы собираетесь перестроить кэш PPTC при следующем запуске для:\n\n{0}\n\nВы уверены, что хотите продолжить?", "sv_SE": "Du är på väg att kölägga en PPTC rebuild vid nästa uppstart av:\n\n{0}\n\nÄr du säker på att du vill fortsätta?", "th_TH": "คุณกำลังตั้งค่าให้มีการสร้าง PPTC ใหม่ในการบูตครั้งถัดไป:\n\n{0}\n\nคุณแน่ใจหรือไม่ว่าต้องการดำเนินการต่อหรือไม่?", @@ -13662,7 +13787,7 @@ "ko_KR": "{0}에서 PPTC 캐시를 지우는 중 오류 발생 : {1}", "no_NO": "Feil under rensing av PPTC cache ved {0}: {1}", "pl_PL": "Błąd czyszczenia cache PPTC w {0}: {1}", - "pt_BR": "Erro apagando cache PPTC em {0}: {1}", + "pt_BR": "Erro ao limpar cache PPTC em {0}: {1}", "ru_RU": "Ошибка очистки кэша PPTC в {0}: {1}", "sv_SE": "Fel vid tömning av PPTC-cache i {0}: {1}", "th_TH": "มีข้อผิดพลาดในการล้างแคช PPTC {0}: {1}", @@ -13688,11 +13813,11 @@ "no_NO": "Du er i ferd med å slette alle PPTC-data fra:\n\n{0}\n\n\nEr du sikker på at du vil fortsette?", "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": "", - "sv_SE": "", + "ru_RU": "Вы собираетесь удалить все данные PPTC из:\n\n{0}\n\nВы уверены, что хотите продолжить?", + "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": "" } @@ -13737,7 +13862,7 @@ "ko_KR": "{0}에서 셰이더 캐시를 삭제하는 중 오류 발생 : {1}", "no_NO": "Feil under tømming av Shader cache ved {0}: {1}", "pl_PL": "Błąd czyszczenia cache Shaderów w {0}: {1}", - "pt_BR": "Erro apagando o cache de Shader em {0}: {1}", + "pt_BR": "Erro ao limpar o cache do Shader em {0}: {1}", "ru_RU": "Ошибка очистки кэша шейдеров в {0}: {1}", "sv_SE": "Fel vid tömning av shader cache i {0}: {1}", "th_TH": "เกิดข้อผิดพลาดในการล้าง แคชแสงเงา {0}: {1}", @@ -13837,7 +13962,7 @@ "ko_KR": "펌웨어 {0} 설치", "no_NO": "Installer fastvare {0}", "pl_PL": "Zainstaluj Firmware {0}", - "pt_BR": "Instalar firmware {0}", + "pt_BR": "Instalar Firmware {0}", "ru_RU": "Установить прошивку {0}", "sv_SE": "Installera firmware {0}", "th_TH": "ติดตั้งเฟิร์มแวร์ {0}", @@ -13992,7 +14117,7 @@ "sv_SE": "En ogiltig nyckelfil hittades i {0}", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Виявлено неправильний файл ключів у теці {0}", "zh_CN": "在 {0} 发现了一个无效的密匙文件", "zh_TW": "找到無效的金鑰檔案 {0}" } @@ -14037,7 +14162,7 @@ "ko_KR": "새로운 키 파일이 설치됩니다.", "no_NO": "Ny Keys-fil vil bli installert.", "pl_PL": "", - "pt_BR": "O novo arquivo Keys será instalado", + "pt_BR": "O novo arquivo Chaves será instalado", "ru_RU": "Будут установлены новые ключи.", "sv_SE": "Ny nyckelfil kommer att installeras.", "th_TH": "", @@ -14238,7 +14363,7 @@ "no_NO": "Du har gjort endringer i denne brukerprofilen som ikke er lagret.", "pl_PL": "Wprowadziłeś zmiany dla tego profilu użytkownika, które nie zostały zapisane.", "pt_BR": "Você fez alterações para este perfil de usuário que não foram salvas.", - "ru_RU": "В эту учетную запись внесены изменения, которые не были сохранены.", + "ru_RU": "В эту учётную запись внесены изменения, которые не были сохранены.", "sv_SE": "Du har gjort ändringar i denna användarprofil som inte har sparats.", "th_TH": "คุณได้ทำการเปลี่ยนแปลงโปรไฟล์ผู้ใช้นี้โดยไม่ได้รับการบันทึก", "tr_TR": "Kullanıcı profilinizde kaydedilmemiş değişiklikler var.", @@ -14637,7 +14762,7 @@ "ko_KR": "경고 - 후단부 스레딩", "no_NO": "Advarsel - Backend Tråd", "pl_PL": "Ostrzeżenie — Wątki Backend", - "pt_BR": "Alerta - Threading da API gráfica", + "pt_BR": "Alerta - Enfileiramento do Renderizador Gráfico", "ru_RU": "Предупреждение: многопоточность в бэкенде", "sv_SE": "Varning - Backend Threading", "th_TH": "คำเตือน - การทำเธรดแบ็กเอนด์", @@ -14762,7 +14887,7 @@ "ko_KR": "그래픽 후단부 다중 스레딩 :", "no_NO": "Grafikk Backend Fleroppgavekjøring", "pl_PL": "Wielowątkowość Backendu Graficznego:", - "pt_BR": "Multithreading da API gráfica:", + "pt_BR": "Multi Enfileiramento do Renderizador Gráfico:", "ru_RU": "Многопоточность графического бэкенда:", "sv_SE": "Multithreading för grafikbakände:", "th_TH": "มัลติเธรด กราฟิกเบื้องหลัง:", @@ -15312,7 +15437,7 @@ "ko_KR": "모든 Amiibo 표시", "no_NO": "Vis alle Amiibo", "pl_PL": "Pokaż Wszystkie Amiibo", - "pt_BR": "Exibir todos os Amiibos", + "pt_BR": "Exibir Todos os Amiibos", "ru_RU": "Показать все Amiibo", "sv_SE": "Visa alla Amiibo", "th_TH": "แสดง Amiibo ทั้งหมด", @@ -15337,7 +15462,7 @@ "ko_KR": "핵 : 무작위 태그 Uuid 사용", "no_NO": "Hack: Bruk tilfeldig tag-Uuid", "pl_PL": "Hack: Użyj losowego UUID tagu", - "pt_BR": "Hack: Usar Uuid de tag aleatório", + "pt_BR": "Hack: Usar Uuid de tag Aleatório", "ru_RU": "Хак: Использовать случайный тег Uuid", "sv_SE": "Hack: Använd slumpmässig tagg för Uuid", "th_TH": "แฮ็ค: สุ่มแท็ก Uuid", @@ -15387,7 +15512,7 @@ "ko_KR": "타이틀 ID", "no_NO": "Tittel ID", "pl_PL": "ID Tytułu", - "pt_BR": "ID do título", + "pt_BR": "ID do Título", "ru_RU": "ID приложения", "sv_SE": "Titel-ID", "th_TH": "ชื่อไอดี", @@ -15412,7 +15537,7 @@ "ko_KR": "컨테이너 경로", "no_NO": "Beholder bane", "pl_PL": "Ścieżka Kontenera", - "pt_BR": "Caminho do container", + "pt_BR": "Caminho do Contêiner", "ru_RU": "Путь к контейнеру", "sv_SE": "Container-sökväg", "th_TH": "คอนเทนเนอร์เก็บไฟล์", @@ -15437,7 +15562,7 @@ "ko_KR": "전체 경로", "no_NO": "Fullstendig bane", "pl_PL": "Pełna Ścieżka", - "pt_BR": "Caminho completo", + "pt_BR": "Caminho Completo", "ru_RU": "Полный путь", "sv_SE": "Fullständig sökväg", "th_TH": "ที่เก็บไฟล์แบบเต็ม", @@ -15462,7 +15587,7 @@ "ko_KR": "모두 제거", "no_NO": "Fjern alle", "pl_PL": "Usuń Wszystkie", - "pt_BR": "Remover todos", + "pt_BR": "Remover Todos", "ru_RU": "Удалить все", "sv_SE": "Ta bort allt", "th_TH": "ลบทั้งหมด", @@ -15487,7 +15612,7 @@ "ko_KR": "모두 활성화", "no_NO": "Aktiver alle", "pl_PL": "Włącz Wszystkie", - "pt_BR": "Habilitar todos", + "pt_BR": "Habilitar Todos", "ru_RU": "Включить все", "sv_SE": "Aktivera allt", "th_TH": "เปิดใช้งานทั้งหมด", @@ -15512,7 +15637,7 @@ "ko_KR": "모두 비활성화", "no_NO": "Deaktiver alle", "pl_PL": "Wyłącz Wszystkie", - "pt_BR": "Desabilitar todos", + "pt_BR": "Desabilitar Todos", "ru_RU": "Отключить все", "sv_SE": "Inaktivera allt", "th_TH": "ปิดใช้งานทั้งหมด", @@ -15562,7 +15687,7 @@ "ko_KR": "언어 변경", "no_NO": "Endre språk", "pl_PL": "Zmień język", - "pt_BR": "Mudar idioma", + "pt_BR": "Mudar Idioma", "ru_RU": "Сменить язык", "sv_SE": "Byt språk", "th_TH": "เปลี่ยนภาษา", @@ -15587,7 +15712,7 @@ "ko_KR": "파일 형식 표시", "no_NO": "Vis Filtyper", "pl_PL": "Pokaż typy plików", - "pt_BR": "Mostrar tipos de arquivo", + "pt_BR": "Mostrar Tipos de Arquivo", "ru_RU": "Показывать форматы файлов", "sv_SE": "Visa filtyper", "th_TH": "แสดงประเภทของไฟล์", @@ -15637,7 +15762,7 @@ "ko_KR": "이름 표시", "no_NO": "Vis navn", "pl_PL": "Pokaż Nazwy", - "pt_BR": "Exibir nomes", + "pt_BR": "Exibir Nomes", "ru_RU": "Показывать названия", "sv_SE": "Visa namn", "th_TH": "แสดงชื่อ", @@ -15667,7 +15792,7 @@ "sv_SE": "Favorit", "th_TH": "สิ่งที่ชื่นชอบ", "tr_TR": "Favori", - "uk_UA": "Вибрані", + "uk_UA": "Обрані", "zh_CN": "收藏", "zh_TW": "我的最愛" } @@ -15762,7 +15887,7 @@ "ko_KR": "오류 창", "no_NO": "Feilvindu", "pl_PL": "Okno Błędu", - "pt_BR": "Janela de erro", + "pt_BR": "Janela de Erro", "ru_RU": "Окно ошибки", "sv_SE": "Felfönster", "th_TH": "หน้าต่างแสดงข้อผิดพลาด", @@ -15788,7 +15913,7 @@ "no_NO": "Velg om Ryujinx skal vises på din \"spillende\" Discord aktivitet eller ikke", "pl_PL": "Wybierz, czy chcesz wyświetlać Ryujinx w swojej \"aktualnie grane\" aktywności Discord", "pt_BR": "Escolha se deseja mostrar Ryujinx ou não na sua atividade do Discord quando estiver usando-o", - "ru_RU": "Включает или отключает отображение статуса \"Играет в игру\" в Discord", + "ru_RU": "Включает или отключает отображение статуса «Играет в игру» в Discord", "sv_SE": "Välj huruvida Ryujinx ska visas på din \"spelar för tillfället\" Discord-aktivitet", "th_TH": "เลือกว่าจะแสดง Ryujinx ในกิจกรรม Discord \"ที่กำลังเล่นอยู่\" ของคุณหรือไม่?", "tr_TR": "Ryujinx'i \"şimdi oynanıyor\" Discord aktivitesinde göstermeyi veya göstermemeyi seçin", @@ -15962,7 +16087,7 @@ "ko_KR": "GUI용 사용자 정의 Avalonia 테마를 사용하여 에뮬레이터 메뉴의 모양 변경", "no_NO": "Bruk et egendefinert Avalonia tema for GUI for å endre utseende til emulatormenyene", "pl_PL": "Użyj niestandardowego motywu Avalonia dla GUI, aby zmienić wygląd menu emulatora", - "pt_BR": "Habilita ou desabilita temas customizados na interface gráfica", + "pt_BR": "Use um tema Avalonia personalizado para a GUI para alterar a aparência dos menus do emulador", "ru_RU": "Включить или отключить пользовательские темы", "sv_SE": "Använd ett anpassat Avalonia-tema för gränssnittet för att ändra utseendet i emulatormenyerna", "th_TH": "ใช้ธีม Avalonia แบบกำหนดเองสำหรับ GUI เพื่อเปลี่ยนรูปลักษณ์ของเมนูโปรแกรมจำลอง", @@ -16092,7 +16217,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如果不確定,請保持關閉狀態。" } @@ -16112,7 +16237,7 @@ "ko_KR": "시스템 지역 변경", "no_NO": "Endre systemregion", "pl_PL": "Zmień Region Systemu", - "pt_BR": "Mudar a região do sistema", + "pt_BR": "Mudar a Região do Sistema", "ru_RU": "Сменяет регион системы", "sv_SE": "Ändra systemets region", "th_TH": "เปลี่ยนภูมิภาคของระบบ", @@ -16137,7 +16262,7 @@ "ko_KR": "시스템 언어 변경", "no_NO": "Endre systemspråk", "pl_PL": "Zmień język systemu", - "pt_BR": "Mudar o idioma do sistema", + "pt_BR": "Mudar o Idioma do Sistema", "ru_RU": "Меняет язык системы", "sv_SE": "Ändra systemets språk", "th_TH": "เปลี่ยนภาษาของระบบ", @@ -16162,7 +16287,7 @@ "ko_KR": "시스템 시간대 변경", "no_NO": "Endre systemtidssone", "pl_PL": "Zmień Strefę Czasową Systemu", - "pt_BR": "Mudar o fuso-horário do sistema", + "pt_BR": "Mudar o Fuso-Horário do Sistema", "ru_RU": "Меняет часовой пояс системы", "sv_SE": "Ändra systemets tidszon", "th_TH": "เปลี่ยนโซนเวลาของระบบ", @@ -16187,7 +16312,7 @@ "ko_KR": "시스템 시간 변경", "no_NO": "Endre systemtid", "pl_PL": "Zmień czas systemowy", - "pt_BR": "Mudar a hora do sistema", + "pt_BR": "Mudar a Data e Hora do Sistema", "ru_RU": "Меняет системное время системы", "sv_SE": "Ändra systemtid", "th_TH": "เปลี่ยนเวลาของระบบ", @@ -16312,7 +16437,7 @@ "ko_KR": "게임을 부팅할 때 손상된 파일을 확인하고, 손상된 파일이 감지되면 로그에 해시 오류를 표시합니다.\n\n성능에 영향을 미치지 않으며 문제 해결에 도움이 됩니다.\n\n모르면 켬으로 두세요.", "no_NO": "Sjekker for korrupte filer ved oppstart av et spill, og dersom korrupte filer oppdages, viser en hashfeil i loggen.\n\nhar ingen innvirkning på ytelsen og er ment å hjelpe med feilsøking.\n\nLa være PÅ hvis usikker.", "pl_PL": "Sprawdza pliki podczas uruchamiania gry i jeśli zostaną wykryte uszkodzone pliki, wyświetla w dzienniku błąd hash.\n\nNie ma wpływu na wydajność i ma pomóc w rozwiązywaniu problemów.\n\nPozostaw WŁĄCZONE, jeśli nie masz pewności.", - "pt_BR": "Habilita ou desabilita verificação de integridade dos arquivos do jogo", + "pt_BR": "Verifica se há arquivos corrompidos ao inicializar um jogo e, se forem detectados, exibe um erro de hash no log.\n\nNão tem impacto no desempenho e tem como objetivo ajudar na solução de problemas.\n\nDeixe LIGADO se não tiver certeza.", "ru_RU": "Проверяет файлы при загрузке игры и если обнаружены поврежденные файлы, выводит сообщение о поврежденном хэше в журнале.\n\nНе влияет на производительность и необходим для помощи в устранении неполадок.\n\nРекомендуется оставить включенным.", "sv_SE": "Letar efter skadade filer när ett spel startas upp, och om skadade filer hittas, visas ett kontrollsummefel i loggen.\n\nHar ingen påverkan på prestandan och är tänkt att hjälpa felsökningen.\n\nLämna PÅ om du är osäker.", "th_TH": "ตรวจสอบไฟล์ที่เสียหายเมื่อบูตเกม และหากตรวจพบไฟล์ที่เสียหาย จะแสดงข้อผิดพลาดของแฮชในบันทึก\n\nไม่มีผลกระทบต่อประสิทธิภาพการทำงานและมีไว้เพื่อช่วยในการแก้ไขปัญหา\n\nเปิดทิ้งไว้หากคุณไม่แน่ใจ", @@ -16337,7 +16462,7 @@ "ko_KR": "오디오 렌더링에 사용되는 백엔드를 변경합니다.\n\nSDL2가 선호되는 반면 OpenAL 및 SoundIO는 대체 수단으로 사용됩니다. 더미에는 소리가 나지 않습니다.\n\n모르면 SDL2로 설정하세요.", "no_NO": "Endrer backend brukt til å gjengi lyd.\n\nSDL2 er foretrukket, mens OpenAL og SoundIO brukes som reserveløsning. Dummy kommer ikke til å ha lyd.\n\nSett til SDL2 hvis usikker.", "pl_PL": "Zmienia backend używany do renderowania dźwięku.\n\nSDL2 jest preferowany, podczas gdy OpenAL i SoundIO są używane jako rezerwy. Dummy nie będzie odtwarzać dźwięku.\n\nW razie wątpliwości ustaw SDL2.", - "pt_BR": "Mudar biblioteca de áudio", + "pt_BR": "Altera o módulo usado para renderizar áudio.\n\nSDL2 é o preferido, enquanto OpenAL e SoundIO são usados como fallbacks. Dummy não terá som.\n\nDefina como SDL2 se não tiver certeza.", "ru_RU": "Изменяет используемый аудио бэкенд для рендера звука.\n\nSDL2 является предпочтительным вариантом, в то время как OpenAL и SoundIO используются в качестве резервных.\n\nРекомендуется использование SDL2.", "sv_SE": "Ändrar bakänden som används för att rendera ljud.\n\nSDL2 är den föredragna, men OpenAL och SoundIO används för att falla tillbaka på. Dummy har inget ljud.\n\nStäll in till SDL2 om du är osäker.", "th_TH": "เปลี่ยนแบ็กเอนด์ที่ใช้ในการเรนเดอร์เสียง\n\nแนะนำเป็น SDL2 ในขณะที่ OpenAL และ SoundIO ถูกใช้เป็นทางเลือกสำรอง ดัมมี่จะไม่มีเสียง\n\nตั้งค่าเป็น SDL2 หากคุณไม่แน่ใจ", @@ -16362,8 +16487,8 @@ "ko_KR": "게스트 메모리 매핑 및 접속 방법을 변경합니다. 에뮬레이트된 CPU 성능에 큰 영향을 미칩니다.\n\n모르면 호스트 확인 안 함으로 설정합니다.", "no_NO": "Endre hvordan gjesteminne tilordnes og åpnes. Påvirker emulator CPU-ytelsen veldig mye.\n\nSett til HOST UNCHECKED hvis usikker.", "pl_PL": "Zmień sposób mapowania i uzyskiwania dostępu do pamięci gości. Znacznie wpływa na wydajność emulowanego procesora.\n\nUstaw na HOST UNCHECKED, jeśli nie masz pewności.", - "pt_BR": "Muda como a memória do sistema convidado é acessada. Tem um grande impacto na performance da CPU emulada.", - "ru_RU": "Меняет разметку и доступ к гостевой памяти. Значительно влияет на производительность процессора.\n\nРекомендуется оставить \"Хост не установлен\"", + "pt_BR": "Altera como a memória do convidado é mapeada e acessada. Afeta muito o desempenho da CPU emulada.\n\nDefina como HÓSPEDE SEM VERIFICAÇÃO se não tiver certeza.", + "ru_RU": "Меняет разметку и доступ к гостевой памяти. Значительно влияет на производительность процессора.\n\nРекомендуется оставить «Хост не установлен»", "sv_SE": "Ändra hur gästminne mappas och ges åtkomst till. Påverkar emulerad CPU-prestanda mycket.\n\nStäll in till \"Värd inte kontrollerad\" om du är osäker.", "th_TH": "เปลี่ยนวิธีการเข้าถึงหน่วยความจำของผู้เยี่ยมชม ส่งผลอย่างมากต่อประสิทธิภาพการทำงานของ CPU ที่จำลอง\n\nตั้งค่าเป็น ไม่ได้ตรวจสอบโฮสต์ หากคุณไม่แน่ใจ", "tr_TR": "Guest hafızasının nasıl tahsis edilip erişildiğini değiştirir. Emüle edilen CPU performansını ciddi biçimde etkiler.\n\nEmin değilseniz HOST UNCHECKED seçeneğine ayarlayın.", @@ -16487,7 +16612,7 @@ "ko_KR": "Switch 개발 모델을 모방하기 위해 8GB DRAM이 포함된 대체 메모리 모드를 활용합니다.\n\n이는 고해상도 텍스처 팩 또는 4K 해상도 모드에만 유용합니다. 성능을 개선하지 않습니다.\n\n모르면 끔으로 두세요.", "no_NO": "Bruker en alternativ minnemodus med 8GiB i DRAM for og etterligne Switch utvikler modeller.\n\nDette er bare nyttig for teksturpakker eller 4k oppløsningsmoduler. Forbedrer IKKE ytelsen.\n\nLa AV hvis usikker.", "pl_PL": "Wykorzystuje alternatywny układ MemoryMode, aby naśladować model rozwojowy Switcha.\n\nJest to przydatne tylko w przypadku pakietów tekstur o wyższej rozdzielczości lub modów w rozdzielczości 4k. NIE poprawia wydajności.\n\nW razie wątpliwości pozostaw WYŁĄCZONE.", - "pt_BR": "Expande a memória do sistema emulado de 4GiB para 6GiB", + "pt_BR": "Utiliza um modo de memória alternativo com 6, 8 ou 12 GB de DRAM para imitar um modelo de desenvolvimento do Switch.\n\nIsso só é útil para pacotes de textura de alta resolução ou mods de resolução 4k. NÃO melhora o desempenho.\n\nDeixe em 4 GB se não tiver certeza.", "ru_RU": "Использует альтернативный макет MemoryMode для имитации использования Nintendo Switch в режиме разработчика.\n\nПолезно только для пакетов текстур с высоким разрешением или модов добавляющих разрешение 4К. Не улучшает производительность.\n\nРекомендуется оставить выключенным.", "sv_SE": "Använder ett alternativt minnesläge med 8GiB av DRAM för att efterlikna en utvecklingsmodell av Switch.\n\nDetta är endast användbart för texturpaket med högre upplösning eller moddar för 4k-upplösning. Det förbättrar INTE prestandan.\n\nLämna AV om du är osäker.", "th_TH": "ใช้รูปแบบ MemoryMode ทางเลือกเพื่อเลียนแบบโมเดลการพัฒนาสวิตช์\n\nสิ่งนี้มีประโยชน์สำหรับแพ็กพื้นผิวที่มีความละเอียดสูงกว่าหรือม็อดที่มีความละเอียด 4k เท่านั้น\n\nปล่อยให้ปิดหากคุณไม่แน่ใจ", @@ -16512,39 +16637,39 @@ "ko_KR": "구현되지 않은 Horizon OS 서비스는 무시됩니다. 특정 게임을 부팅할 때, 발생하는 충돌을 우회하는 데 도움이 될 수 있습니다.\n\n모르면 끔으로 두세요.", "no_NO": "Ignorerer ikke implementerte Horisont OS-tjenester. Dette kan hjelpe med å omgå krasj ved oppstart av enkelte spill.\n\nLa AV hvis du er usikker.", "pl_PL": "Ignoruje niezaimplementowane usługi Horizon OS. Może to pomóc w ominięciu awarii podczas uruchamiania niektórych gier.\n\nW razie wątpliwości pozostaw WYŁĄCZONE.", - "pt_BR": "Habilita ou desabilita a opção de ignorar serviços não implementados", + "pt_BR": "Ignora serviços não implementados do Horizon OS. Isso pode ajudar a contornar travamentos ao inicializar certos jogos.\n\nDeixe OFF se não tiver certeza.", "ru_RU": "Игнорирует нереализованные сервисы Horizon в новых прошивках. Эта настройка поможет избежать вылеты при запуске определенных игр.\n\nРекомендуется оставить выключенным.", "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如果不確定,請保持關閉狀態。" } }, { - "ID": "IgnoreAppletTooltip", + "ID": "IgnoreControllerAppletTooltip", "Translations": { - "ar_SA": "لن يظهر مربع الحوار الخارجي \"تطبيق وحدة التحكم\" إذا تم فصل لوحة الألعاب أثناء اللعب. ولن تظهر مطالبة بإغلاق مربع الحوار أو إعداد وحدة تحكم جديدة. وبمجرد إعادة توصيل وحدة التحكم التي تم فصلها سابقًا، ستستأنف اللعبة تلقائيًا.", - "de_DE": "Der externe Dialog \"Controller-Applet\" wird nicht angezeigt, wenn das Gamepad während des Spiels getrennt wird. Es erfolgt keine Aufforderung, den Dialog zu schließen oder einen neuen Controller einzurichten. Sobald der zuvor getrennte Controller wieder angeschlossen wird, wird das Spiel automatisch fortgesetzt.", - "el_GR": "Το εξωτερικό παράθυρο διαλόγου \"Ελεγκτής μικροεφαρμογής\" δεν θα εμφανιστεί εάν το gamepad αποσυνδεθεί κατά τη διάρκεια του παιχνιδιού. Δεν θα σας ζητηθεί να κλείσετε το παράθυρο διαλόγου ή να ρυθμίσετε έναν νέο ελεγκτή. Μόλις επανασυνδεθεί το χειριστήριο που είχε αποσυνδεθεί προηγουμένως, το παιχνίδι θα συνεχιστεί αυτόματα.", - "en_US": "The external dialog \"Controller Applet\" will not appear if the gamepad is disconnected during gameplay. There will be no prompt to close the dialog or set up a new controller. Once the previously disconnected controller is reconnected, the game will automatically resume.", - "es_ES": "El cuadro de diálogo externo \"Applet del controlador\" no aparecerá si el gamepad se desconecta durante el juego. No aparecerá ningún mensaje para cerrar el cuadro de diálogo o configurar un nuevo controlador. Una vez que se vuelva a conectar el controlador que se había desconectado anteriormente, el juego se reanudará automáticamente.", - "fr_FR": "La boîte de dialogue externe \"Programme Manette\" n'apparaîtra pas si la manette est déconnectée en jeu. Il n'y aura aucune boîte de dialogue ouverte pour configurer une nouvelle manette. Une fois que la manette précédemment déconnectée est reconnectée, le jeu reprendra automatiquement. \n\nLaissez désactivé en cas d'incertitude.", - "he_IL": "תיבת הדו-שיח החיצונית \"Controller Applet\" לא תופיע אם ה-Gamepad מנותק במהלך המשחק. לא תהיה הנחיה לסגור את תיבת הדו-שיח או להגדיר בקר חדש. ברגע שהבקר שנותק בעבר יתחבר מחדש, המשחק יתחדש אוטומטית.", - "it_IT": "La finestra di dialogo esterna \"Controller Applet\" non apparirà se il gamepad viene disconnesso durante il gioco. Non ci sarà alcun prompt per chiudere la finestra di dialogo o impostare un nuovo controller. Una volta che il controller disconnesso in precedenza viene ricollegato, il gioco riprenderà automaticamente.", - "ja_JP": "ゲームプレイ中にゲームパッドが切断された場合、外部ダイアログ「コントローラーアプレット」は表示されません。このダイアログを閉じるか、新しいコントローラーを設定するように求めるプロンプトは表示されません。以前に切断されたコントローラーが再接続されると、ゲームは自動的に再開されます。", - "ko_KR": "게임 플레이 중에 게임패드 연결이 끊어지면 외부 대화 상자 \"컨트롤러 애플릿\"이 나타나지 않습니다. 대화 상자를 닫거나 새 컨트롤러를 설정하라는 메시지가 표시되지 않습니다. 이전에 연결이 끊어진 컨트롤러가 다시 연결되면 게임이 자동으로 다시 시작됩니다.", - "no_NO": "Den eksterne dialogboksen «Controller Applet» vises ikke hvis gamepaden kobles fra under spilling. Du blir ikke bedt om å lukke dialogboksen eller konfigurere en ny kontroller. Når den tidligere frakoblede kontrolleren er koblet til igjen, fortsetter spillet automatisk.", - "pl_PL": "Zewnętrzny dialog \"Controller Applet\" nie pojawi się, jeśli gamepad zostanie odłączony podczas rozgrywki. Nie pojawi się monit o zamknięcie dialogu lub skonfigurowanie nowego kontrolera. Po ponownym podłączeniu poprzednio odłączonego kontrolera gra zostanie automatycznie wznowiona.", - "pt_BR": "O diálogo externo \"Controller Applet\" não aparecerá se o gamepad for desconectado durante o jogo. Não haverá prompt para fechar o diálogo ou configurar um novo controle. Assim que o controle desconectado anteriormente for reconectado, o jogo será retomado automaticamente.", - "ru_RU": "Внешний диалог \"Апплет контроллера\" не появится, если геймпад будет отключен во время игры. Не будет предложено закрыть диалог или настроить новый контроллер. После повторного подключения ранее отключенного контроллера игра автоматически возобновится.", - "sv_SE": "Den externa dialogen \"Handkontroller-applet\" kommer inte att visas om din gamepad är frånkopplad under spel. Det finns ingen fråga att stänga dialogen eller konfigurera en ny handkontroller. När den tidigare frånkopplade handkontrollern återansluts så kommer spelet att automatiskt återupptas.", - "th_TH": "กล่องโต้ตอบภายนอก \"แอปเพล็ตตัวควบคุม\" จะไม่ปรากฏขึ้นหากแป้นเกมถูกตัดการเชื่อมต่อระหว่างการเล่นเกม จะไม่มีข้อความแจ้งให้ปิดกล่องโต้ตอบหรือตั้งค่าตัวควบคุมใหม่ เมื่อเชื่อมต่อคอนโทรลเลอร์ที่ตัดการเชื่อมต่อก่อนหน้านี้อีกครั้ง เกมจะดำเนินการต่อโดยอัตโนมัติ", - "tr_TR": "Oyun sırasında oyun kumandasının bağlantısı kesilirse, harici \"Controller Applet\" iletişim kutusu görünmez. İletişim kutusunu kapatma veya yeni bir kumanda ayarlama isteği olmaz. Daha önce bağlantısı kesilen kumanda tekrar bağlandığında oyun otomatik olarak devam eder.", - "uk_UA": "Зовнішнє діалогове вікно \"Аплет контролера\" не з’являтиметься, якщо геймпад буде від’єднано під час гри. Не буде запиту закрити діалогове вікно чи налаштувати новий контролер. Після повторного підключення раніше від’єднаного контролера гра автоматично відновиться.", - "zh_CN": "如果游戏手柄在游戏过程中断开连接,则不会出现外部对话框“控制器小程序”。不会提示关闭对话框或设置新控制器。一旦先前断开连接的控制器重新连接,游戏将自动恢复。", - "zh_TW": "如果遊戲手把在遊戲過程中斷開連接,則外部對話方塊「控制器小程式」將不會出現。不會提示關閉對話方塊或設定新控制器。一旦先前斷開的控制器重新連接,遊戲將自動恢復。" + "ar_SA": "", + "de_DE": "", + "el_GR": "", + "en_US": "The Controller Applet dialog will not appear if the gamepad is disconnected while an application is running.\n\nLeave OFF if unsure.", + "es_ES": "", + "fr_FR": "", + "he_IL": "", + "it_IT": "", + "ja_JP": "", + "ko_KR": "", + "no_NO": "", + "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": "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": "Діалогове вікно Аплету Контролера не з'явиться, якщо геймпад було відключено під час роботи програми.\n\nЗалиште вимкненим якщо не впевнені.", + "zh_CN": "在应用程序运行时如果游戏手柄断开连接则不会显示控制器小程序对话框。\n\n如果不确定,请保持关闭状态。", + "zh_TW": "" } }, { @@ -16562,12 +16687,12 @@ "ko_KR": "2번째 스레드에서 그래픽 후단부 명령을 실행합니다.\n\n셰이더 컴파일 속도를 높이고, 끊김 현상을 줄이며, 자체 다중 스레딩 지원 없이 GPU 드라이버의 성능을 향상시킵니다. 다중 스레딩이 있는 드라이버에서 성능이 좀 더 좋습니다.\n\n모르면 자동으로 설정합니다.", "no_NO": "Utfører grafikkbackend kommandoer på en annen tråd.\n\nØker hastigheten for shaderkomprimering, reduserer hakking og forbedrer ytelsen til GPU-drivere uten å spre støtten fra sine egne. Litt bedre ytelse på drivere med flertråd.\n\nSett for å AUTO hvis usikker.", "pl_PL": "Wykonuje polecenia backend'u graficznego w drugim wątku.\n\nPrzyspiesza kompilację shaderów, zmniejsza zacinanie się i poprawia wydajność sterowników GPU bez własnej obsługi wielowątkowości. Nieco lepsza wydajność w sterownikach z wielowątkowością.\n\nUstaw na AUTO, jeśli nie masz pewności.", - "pt_BR": "Habilita multithreading do backend gráfico", + "pt_BR": "Executa comandos do renderizador gráfico em um segundo thread.\n\nAcelera a compilação de shader, reduz a gagueira e melhora o desempenho em drivers de GPU sem suporte multithreading próprio. Desempenho ligeiramente melhor em drivers com multithreading.\n\nDefina como AUTO se não tiver certeza.", "ru_RU": "Выполняет команды графического бэкенда на втором потоке.\n\nУскоряет компиляцию шейдеров, уменьшает статтеры и повышает производительность на драйверах видеоадаптера без поддержки многопоточности. Производительность на драйверах с многопоточностью немного выше.\n\nРекомендуется оставить Автоматически.", "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如果不確定,請設定為自動。" } @@ -16587,12 +16712,12 @@ "ko_KR": "2번째 스레드에서 그래픽 후단부 명령을 실행합니다.\n\n셰이더 컴파일 속도를 높이고 끊김 현상을 줄이며 자체 다중 스레딩 지원 없이 GPU 드라이버의 성능을 향상시킵니다. 다중 스레딩이 있는 드라이버에서 성능이 좀 더 좋습니다.\n\n모르면 자동으로 설정합니다.", "no_NO": "Utfører grafikkbackend kommandoer på en annen tråd.\n\nØker hastigheten for shaderkomprimering, reduserer hakking og forbedrer ytelsen til GPU-drivere uten flertråd støtte. Litt bedre ytelse på drivere med flertråd.\n\nSett for å AUTO hvis usikker.", "pl_PL": "Wykonuje polecenia backend'u graficznego w drugim wątku.\n\nPrzyspiesza kompilację shaderów, zmniejsza zacinanie się i poprawia wydajność sterowników GPU bez własnej obsługi wielowątkowości. Nieco lepsza wydajność w sterownikach z wielowątkowością.\n\nUstaw na AUTO, jeśli nie masz pewności.", - "pt_BR": "Executa comandos do backend gráfico em uma segunda thread. Permite multithreading em tempo de execução da compilação de shader, diminui os travamentos, e melhora performance em drivers sem suporte embutido a multithreading. Pequena variação na performance máxima em drivers com suporte a multithreading. Ryujinx pode precisar ser reiniciado para desabilitar adequadamente o multithreading embutido do driver, ou você pode precisar fazer isso manualmente para ter a melhor performance.", + "pt_BR": "Executa comandos do renderizador gráfico em um segundo thread.\n\nAcelera a compilação de shader, reduz a gagueira e melhora o desempenho em drivers de GPU sem suporte multithreading próprio. Desempenho ligeiramente melhor em drivers com multithreading.\n\nDefina como AUTO se não tiver certeza.", "ru_RU": "Выполняет команды графического бэкенда на втором потоке.\n\nУскоряет компиляцию шейдеров, уменьшает статтеры и повышает производительность на драйверах видеоадаптера без поддержки многопоточности. Производительность на драйверах с многопоточностью немного выше.\n\nРекомендуется оставить Автоматически.", "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如果不確定,請設定為自動。" } @@ -16612,12 +16737,12 @@ "ko_KR": "후속 실행 시 끊김 현상을 줄이는 디스크 셰이더 캐시를 저장합니다.\n\n모르면 켬으로 두세요.", "no_NO": "Lagrer en disk shader cache som reduserer hakking jo flere ganger du spiller.\n\nLa være PÅ om usikker.", "pl_PL": "Zapisuje pamięć podręczną shaderów na dysku, co zmniejsza zacinanie się w kolejnych uruchomieniach.\n\nPozostaw WŁĄCZONE, jeśli nie masz pewności.", - "pt_BR": "Salva um cache de shader de disco que reduz a trepidação em execuções subsequentes.\n\nDeixe LIGADO se não tiver certeza.", + "pt_BR": "Salva um cache de shader no disco que reduz a trepidação em execuções subsequentes.\n\nDeixe LIGADO se não tiver certeza.", "ru_RU": "Сохраняет кэш шейдеров на диске, для уменьшения статтеров при последующих запусках.\n\nРекомендуется оставить включенным.", "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如果不確定,請保持開啟狀態。" } @@ -16638,7 +16763,7 @@ "no_NO": "Dobler spillets gjengivelse.\n\nNoen få spill fungerer kanskje ikke med dette aktivert og kan se veldig pikselert ut selv når gjengivelsen er økt; for de spillene, så kan det hende du må bruke modifikasjoner som fjerner anti-aliasing eller som øker den interne gjengivelsen. For og bruke sistnenvte, så vil du helst bruke \"Native\".\n\nHa til tanke at 4x er unødig for virituelt alle maskiner.", "pl_PL": "", "pt_BR": "Multiplica a resolução de renderização do jogo.\n\nAlguns jogos podem não funcionar bem com essa opção e apresentar uma aparência pixelada, mesmo com o aumento da resolução; para esses jogos, talvez seja necessário encontrar mods que removam o anti-aliasing ou aumentem a resolução de renderização interna. Ao usar a segunda opção, provavelmente desejará selecionar Nativa.\n\nEssa opção pode ser alterada enquanto um jogo está em execução, clicando em \"Aplicar\" abaixo; basta mover a janela de configurações para o lado e experimentar até encontrar o visual preferido para o jogo.\n\nLembre-se de que 4x é exagerado para praticamente qualquer configuração.", - "ru_RU": "Увеличивает разрешение рендера игры.\n\nНекоторые игры могут не работать с этой настройкой и выглядеть смазано даже когда разрешение увеличено. Для таких игр может потребоваться установка модов, которые убирают сглаживание или увеличивают разрешение рендеринга. \nДля использования последнего, вам нужно будет выбрать опцию \"Нативное\".\n\nЭта опция может быть изменена во время игры по нажатию кнопки \"Применить\" ниже. Вы можете просто переместить окно настроек в сторону и поэкспериментировать, пока не подберете подходящие настройки для конкретной игры.\n\nИмейте в виду, что \"4x\" является излишеством.", + "ru_RU": "Увеличивает разрешение рендера игры.\n\nНекоторые игры могут не работать с этой настройкой и выглядеть смазано даже когда разрешение увеличено. Для таких игр может потребоваться установка модов, которые убирают сглаживание или увеличивают разрешение рендеринга. \nДля использования последнего, вам нужно будет выбрать опцию «Нативное».\n\nЭта опция может быть изменена во время игры по нажатию кнопки «Применить» ниже. Вы можете просто переместить окно настроек в сторону и поэкспериментировать, пока не подберете подходящие настройки для конкретной игры.\n\nИмейте в виду, что «4x» является излишеством.", "sv_SE": "Multiplicerar spelets renderingsupplösning.\n\nNågra spel kanske inte fungerar med detta och ser pixelerade ut även när upplösningen ökas; för dessa spel så kan du behöva hitta moddar som tar bort anti-aliasing eller som ökar deras interna renderingsupplösning. För att använda det senare, kommer du sannolikt vilja välja Inbyggd.\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\nTänk på att 4x är overkill för praktiskt taget alla maskiner.", "th_TH": "คูณความละเอียดการเรนเดอร์ของเกม\n\nเกมบางเกมอาจไม่สามารถใช้งานได้และดูเป็นพิกเซลแม้ว่าความละเอียดจะเพิ่มขึ้นก็ตาม สำหรับเกมเหล่านั้น คุณอาจต้องค้นหาม็อดที่ลบรอยหยักของภาพหรือเพิ่มความละเอียดในการเรนเดอร์ภายใน หากต้องการใช้อย่างหลัง คุณอาจต้องเลือก Native\n\nตัวเลือกนี้สามารถเปลี่ยนแปลงได้ในขณะที่เกมกำลังทำงานอยู่โดยคลิก \"นำมาใช้\" ด้านล่าง คุณสามารถย้ายหน้าต่างการตั้งค่าไปด้านข้างและทดลองจนกว่าคุณจะพบรูปลักษณ์ที่คุณต้องการสำหรับเกม\n\nโปรดทราบว่า 4x นั้นเกินความจำเป็นสำหรับการตั้งค่าแทบทุกประเภท", "tr_TR": "", @@ -16737,7 +16862,7 @@ "ko_KR": "그래픽 셰이더 덤프 경로", "no_NO": "Grafikk Shader Dump bane", "pl_PL": "Ścieżka Zrzutu Shaderów Grafiki", - "pt_BR": "Diretòrio de despejo de shaders", + "pt_BR": "Diretório de Despejo de Shaders", "ru_RU": "Путь с дампами графических шейдеров", "sv_SE": "Sökväg för Graphics Shaders Dump", "th_TH": "ที่เก็บ ดัมพ์ไฟล์เชเดอร์", @@ -16762,7 +16887,7 @@ "ko_KR": "디스크의 로그 파일에 콘솔 기록을 저장합니다. 성능에 영향을 주지 않습니다.", "no_NO": "Lagrer konsoll-logging til en loggfil på harddisken. Påvirker ikke ytelsen.", "pl_PL": "Zapisuje logowanie konsoli w pliku dziennika na dysku. Nie wpływa na wydajność.", - "pt_BR": "Habilita ou desabilita log para um arquivo no disco", + "pt_BR": "Salva o log do console em um arquivo de log no disco. Não afeta o desempenho.", "ru_RU": "Включает ведение журнала в файл на диске. Не влияет на производительность.", "sv_SE": "Sparar konsolloggning till en loggfil på disk. Påverkar inte prestandan.", "th_TH": "บันทึกประวัติคอนโซลลงในไฟล์บันทึก จะไม่ส่งผลกระทบต่อประสิทธิภาพการทำงาน", @@ -16787,7 +16912,7 @@ "ko_KR": "콘솔에 조각 기록 메시지를 출력합니다. 성능에 영향을 주지 않습니다.", "no_NO": "Skriver ut log meldinger i konsollen. Påvirker ikke ytelsen.", "pl_PL": "Wyświetla w konsoli skrótowe komunikaty dziennika. Nie wpływa na wydajność.", - "pt_BR": "Habilita ou desabilita exibição de mensagens de stub", + "pt_BR": "Imprime mensagens de log stub no console. Não afeta o desempenho.", "ru_RU": "Включает ведение журнала-заглушки. Не влияет на производительность.", "sv_SE": "Skriver ut stubbloggmeddelanden i konsollen. Påverkar inte prestandan.", "th_TH": "พิมพ์ข้อความประวัติในคอนโซล จะไม่ส่งผลกระทบต่อประสิทธิภาพการทำงาน", @@ -16812,7 +16937,7 @@ "ko_KR": "콘솔에 정보 기록 메시지를 출력합니다. 성능에 영향을 주지 않습니다.", "no_NO": "Skriver ut info loggmeldinger i konsollen. Påvirker ikke ytelse.", "pl_PL": "Wyświetla komunikaty dziennika informacyjnego w konsoli. Nie wpływa na wydajność.", - "pt_BR": "Habilita ou desabilita exibição de mensagens informativas", + "pt_BR": "Imprime mensagens de log de informações no console. Não afeta o desempenho.", "ru_RU": "Включает вывод сообщений информационного журнала в консоль. Не влияет на производительность.", "sv_SE": "Skriver ut informationsloggmeddelanden i konsollen. Påverkar inte prestandan.", "th_TH": "พิมพ์ข้อความบันทึกข้อมูลในคอนโซล จะไม่ส่งผลกระทบต่อประสิทธิภาพการทำงาน", @@ -16837,7 +16962,7 @@ "ko_KR": "콘솔에 경고 기록 메시지를 출력합니다. 성능에 영향을 주지 않습니다.", "no_NO": "Skriver ut varselloggmeldinger i konsollen. påvirker ikke ytelsen.", "pl_PL": "Wyświetla komunikaty dziennika ostrzeżeń w konsoli. Nie wpływa na wydajność.", - "pt_BR": "Habilita ou desabilita exibição de mensagens de alerta", + "pt_BR": "Imprime mensagens de log de aviso no console. Não afeta o desempenho.", "ru_RU": "Включает вывод сообщений журнала предупреждений в консоль. Не влияет на производительность.", "sv_SE": "Skriver ut varningsloggmeddelanden i konsollen. Påverkar inte prestandan.", "th_TH": "พิมพ์ข้อความประวัติการเตือนในคอนโซล จะไม่ส่งผลกระทบต่อประสิทธิภาพการทำงาน", @@ -16862,7 +16987,7 @@ "ko_KR": "콘솔에 오류 기록 메시지를 출력합니다. 성능에 영향을 주지 않습니다.", "no_NO": "Skriver ut feilloggmeldinger i konsollen. Påvirker ikke ytelse.", "pl_PL": "Wyświetla w konsoli komunikaty dziennika błędów. Nie wpływa na wydajność.", - "pt_BR": "Habilita ou desabilita exibição de mensagens de erro", + "pt_BR": "Imprime mensagens de log de erro no console. Não afeta o desempenho.", "ru_RU": "Включает вывод сообщений журнала ошибок. Не влияет на производительность.", "sv_SE": "Skriver ut felloggmeddelanden i konsollen. Påverkar inte prestandan.", "th_TH": "พิมพ์ข้อความบันทึกข้อผิดพลาดในคอนโซล จะไม่ส่งผลกระทบต่อประสิทธิภาพการทำงาน", @@ -16887,7 +17012,7 @@ "ko_KR": "콘솔에 추적 기록 메시지를 출력합니다. 성능에 영향을 주지 않습니다.", "no_NO": "Skriver ut sporbare loggmeldinger i konsollen. påvirker ikke ytelsen.", "pl_PL": "Wyświetla komunikaty dziennika śledzenia w konsoli. Nie wpływa na wydajność.", - "pt_BR": "Habilita ou desabilita exibição de mensagens de rastreamento", + "pt_BR": "Imprime mensagens de log de rastreamento no console. Não afeta o desempenho.", "ru_RU": "Выводит сообщения журнала трассировки в консоли. Не влияет на производительность.", "sv_SE": "Skriver ut spårloggmeddelanden i konsollen. Påverkar inte prestandan.", "th_TH": "พิมพ์ข้อความประวัติการติดตามในคอนโซล ไม่ส่งผลกระทบต่อประสิทธิภาพการทำงาน", @@ -16912,7 +17037,7 @@ "ko_KR": "콘솔에 게스트 로그 메시지를 출력합니다. 성능에 영향을 주지 않습니다.", "no_NO": "Skriver ut gjesteloggmeldinger i konsollen. påvirker ikke ytelsen.", "pl_PL": "Wyświetla komunikaty dziennika gości w konsoli. Nie wpływa na wydajność.", - "pt_BR": "Habilita ou desabilita exibição de mensagens do programa convidado", + "pt_BR": "Imprime mensagens de log do convidado no console. Não afeta o desempenho.", "ru_RU": "Включает вывод сообщений гостевого журнала. Не влияет на производительность.", "sv_SE": "Skriver ut gästloggmeddelanden i konsollen. Påverkar inte prestandan.", "th_TH": "พิมพ์ข้อความประวัติของผู้เยี่ยมชมในคอนโซล ไม่ส่งผลกระทบต่อประสิทธิภาพการทำงาน", @@ -16937,7 +17062,7 @@ "ko_KR": "콘솔에 파일 접속 기록 메시지를 출력합니다.", "no_NO": "Skriver ut filtilgang til loggmeldinger i konsollen.", "pl_PL": "Wyświetla w konsoli komunikaty dziennika dostępu do plików.", - "pt_BR": "Habilita ou desabilita exibição de mensagens do acesso de arquivos", + "pt_BR": "Imprime mensagens de log de acesso a arquivos no console.", "ru_RU": "Включает вывод сообщений журнала доступа к файлам.", "sv_SE": "Skriver ut loggmeddelanden för filåtkomst i konsollen.", "th_TH": "พิมพ์ข้อความบันทึกการเข้าถึงไฟล์ในคอนโซล", @@ -16962,12 +17087,12 @@ "ko_KR": "콘솔에 파일 시스템 접속 기록 출력을 활성화합니다. 가능한 모드는 0-3", "no_NO": "Aktiverer FS tilgang loggutgang til konsollen. Mulige moduser er 0-3", "pl_PL": "Włącza wyjście dziennika dostępu FS do konsoli. Możliwe tryby to 0-3", - "pt_BR": "Habilita exibição de mensagens de acesso ao sistema de arquivos no console. Modos permitidos são 0-3", + "pt_BR": "Habilita a saída do log de acesso FS para o console. Os modos possíveis são 0-3", "ru_RU": "Включает вывод журнала доступа к файловой системе. Возможные режимы: 0-3", "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" } @@ -17012,12 +17137,12 @@ "ko_KR": "", "no_NO": "Skriver ut Avalonia (UI)-loggmeldinger i konsollen.", "pl_PL": "", - "pt_BR": "Imprimir mensagens de log do Avalonia (IU) no console.", - "ru_RU": "", - "sv_SE": "", + "pt_BR": "Imprime mensagens de log do Avalonia (UI) no console.", + "ru_RU": "Выводит сообщения журнала Avalonia (интерфейс) в консоли.", + "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": "" } @@ -17037,7 +17162,7 @@ "ko_KR": "적절한 기록 수준이 활성화되어 있어야 함", "no_NO": "Krever riktige loggnivåer aktivert", "pl_PL": "Wymaga włączonych odpowiednich poziomów logów", - "pt_BR": "Requer que os níveis de log apropriados estejaam habilitados", + "pt_BR": "Requer níveis de log apropriados habilitados", "ru_RU": "Требует включения соответствующих уровней ведения журнала", "sv_SE": "Kräver att lämpliga loggnivåer aktiveras", "th_TH": "จำเป็นต้องเปิดใช้งานระดับบันทึกที่เหมาะสม", @@ -17062,7 +17187,7 @@ "ko_KR": "콘솔에 디버그 기록 메시지를 출력합니다.\n\n담당자가 특별히 요청한 경우에만 이 기능을 사용하십시오. 로그를 읽기 어렵게 만들고 에뮬레이터 성능을 저하시킬 수 있기 때문입니다.", "no_NO": "Skriver ut loggmeldinger i konsollen.\n\nBruk bare dette hvis et medlem har gitt spesifikke instruksjoner, siden det vil gjøre loggene vanskelig å lese og forverre emulatorytelse.", "pl_PL": "Wyświetla komunikaty dziennika debugowania w konsoli.\n\nUżywaj tego tylko na wyraźne polecenie członka załogi, ponieważ utrudni to odczytanie dzienników i pogorszy wydajność emulatora.", - "pt_BR": "Habilita exibição de mensagens de depuração", + "pt_BR": "Imprime mensagens de log de depuração no console.\n\nUse isso somente se for especificamente instruído por um membro da equipe, pois isso tornará os logs difíceis de ler e piorará o desempenho do emulador.", "ru_RU": "Выводит журнал сообщений отладки в консоли.\n\nИспользуйте только в случае просьбы разработчика, так как включение этой функции затруднит чтение журналов и ухудшит работу эмулятора.", "sv_SE": "Skriver ut felsökningsloggmeddelanden i konsolen.\n\nAnvänd endast detta om det är specifikt instruerat av en medarbetare, eftersom det kommer att göra loggar svåra att läsa och försämra emulatorprestanda.", "th_TH": "พิมพ์ข้อความประวัติการแก้ไขข้อบกพร่องในคอนโซล\n\nใช้สิ่งนี้เฉพาะเมื่อได้รับคำแนะนำจากผู้ดูแลเท่านั้น เนื่องจากจะทำให้บันทึกอ่านยากและทำให้ประสิทธิภาพของโปรแกรมจำลองแย่ลง", @@ -17087,7 +17212,7 @@ "ko_KR": "파일 탐색기를 열어 불러올 Switch 호환 파일을 선택", "no_NO": "Åpne filutforsker for å velge en Switch kompatibel fil å laste", "pl_PL": "Otwórz eksplorator plików, aby wybrać plik kompatybilny z Switch do wczytania", - "pt_BR": "Abre o navegador de arquivos para seleção de um arquivo do Switch compatível a ser carregado", + "pt_BR": "Abre um explorador de arquivos para escolher um arquivo compatível com o Switch para carregar", "ru_RU": "Открывает файловый менеджер для выбора файла, совместимого с Nintendo Switch.", "sv_SE": "Öppna en filutforskare för att välja en Switch-kompatibel fil att läsa in", "th_TH": "เปิดตัวสำรวจไฟล์เพื่อเลือกไฟล์ที่เข้ากันได้กับ Switch ที่จะโหลด", @@ -17112,7 +17237,7 @@ "ko_KR": "Switch와 호환되는 압축 해제된 앱을 선택하여 불러오려면 파일 탐색기를 엽니다.", "no_NO": "Åpne en filutforsker for å velge en Switch kompatibel, upakket applikasjon for å laste", "pl_PL": "Otwórz eksplorator plików, aby wybrać zgodną z Switch, rozpakowaną aplikację do załadowania", - "pt_BR": "Abre o navegador de pastas para seleção de pasta extraída do Switch compatível a ser carregada", + "pt_BR": "Abre um explorador de arquivos para escolher um aplicativo descompactado compatível com o Switch para carregar", "ru_RU": "Открывает файловый менеджер для выбора распакованного приложения, совместимого с Nintendo Switch.", "sv_SE": "Öppna en filutforskare för att välja en Switch-kompatibel, uppackad applikation att läsa in", "th_TH": "เปิดตัวสำรวจไฟล์เพื่อเลือกไฟล์ที่เข้ากันได้กับ Switch ที่จะโหลด", @@ -17137,7 +17262,7 @@ "ko_KR": "파일 탐색기를 열어 DLC를 일괄 불러오기할 폴더를 하나 이상 선택", "no_NO": "Åpne en filutforsker for å velge en eller flere mapper å laste inn DLC fra", "pl_PL": "", - "pt_BR": "Abrir um explorador de arquivos para escolher uma ou mais pastas para carregar DLC em massa", + "pt_BR": "Abre um explorador de arquivos para escolher uma ou mais pastas para carregar DLC em massa", "ru_RU": "Открывает проводник, для выбора одной или нескольких папок для массовой загрузки DLC", "sv_SE": "Öppna en filutforskare för att välja en eller flera mappar att läsa in alla DLC från", "th_TH": "เปิดตัวสำรวจไฟล์เพื่อเลือกหนึ่งโฟลเดอร์ขึ้นไปเพื่อโหลด DLC จำนวนมาก", @@ -17162,12 +17287,12 @@ "ko_KR": "파일 탐색기를 열어 하나 이상의 폴더를 선택하여 대량으로 타이틀 업데이트 불러오기", "no_NO": "Åpne en filutforsker for å velge en eller flere mapper som du vil laste inn titteloppdateringer fra", "pl_PL": "", - "pt_BR": "Abrir o explorador de arquivos para selecionar uma ou mais pastas e carregar atualizações de jogo em massa.", + "pt_BR": "Abre um explorador de arquivos para escolher uma ou mais pastas para carregar em massa as atualizações de títulos", "ru_RU": "Открывает проводник, чтобы выбрать одну или несколько папок для массовой загрузки обновлений приложений", "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": "開啟檔案總管,選擇一個或多個資料夾來大量載入遊戲更新" } @@ -17187,7 +17312,7 @@ "ko_KR": "Ryujinx 파일 시스템 폴더 열기", "no_NO": "Åpne Ryujinx filsystem-mappen", "pl_PL": "Otwórz folder systemu plików Ryujinx", - "pt_BR": "Abre o diretório do sistema de arquivos do Ryujinx", + "pt_BR": "Abre a pasta do sistema de arquivos Ryujinx", "ru_RU": "Открывает папку с файлами Ryujinx", "sv_SE": "Öppna Ryujinx-filsystemsmappen", "th_TH": "เปิดโฟลเดอร์ระบบไฟล์ Ryujinx", @@ -17197,6 +17322,31 @@ "zh_TW": "開啟 Ryujinx 檔案系統資料夾" } }, + { + "ID": "OpenScreenshotFolderTooltip", + "Translations": { + "ar_SA": "", + "de_DE": "", + "el_GR": "", + "en_US": "Open Ryujinx screenshots folder", + "es_ES": "", + "fr_FR": "", + "he_IL": "", + "it_IT": "", + "ja_JP": "", + "ko_KR": "", + "no_NO": "", + "pl_PL": "", + "pt_BR": "Abre a pasta de capturas de tela do Ryujinx", + "ru_RU": "Открывает папку скриншотов Ryujinx", + "sv_SE": "Öppna Ryujinx skärmbildsmapp", + "th_TH": "", + "tr_TR": "", + "uk_UA": "Відкрити теку куди зберігаються скріншоти Ryujinx", + "zh_CN": "打开 Ryujinx 截图文件夹", + "zh_TW": "" + } + }, { "ID": "OpenRyujinxLogsTooltip", "Translations": { @@ -17212,12 +17362,12 @@ "ko_KR": "로그가 기록되는 폴더 열기", "no_NO": "Åpner mappen hvor logger er lagret", "pl_PL": "Otwiera folder, w którym zapisywane są logi", - "pt_BR": "Abre o diretório onde os logs são salvos", - "ru_RU": "Открывает папку в которую записываются логи", + "pt_BR": "Abre a pasta onde os logs são gravados", + "ru_RU": "Открывает папку, в которую записываются логи", "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": "開啟日誌被寫入的資料夾" } @@ -17242,7 +17392,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" } @@ -17287,8 +17437,8 @@ "ko_KR": "사용자 프로필 관리자 창 열기", "no_NO": "Åpne vindu for brukerprofiler", "pl_PL": "Otwórz okno Menedżera Profili Użytkownika", - "pt_BR": "Abrir janela de gerenciamento de perfis", - "ru_RU": "Открыть менеджер учетных записей", + "pt_BR": "Abrir Janela de Gerenciamento de Perfis", + "ru_RU": "Открыть менеджер учётных записей", "sv_SE": "Öppna hanterare för användarprofiler", "th_TH": "เปิดหน้าต่างตัวจัดการโปรไฟล์ผู้ใช้", "tr_TR": "Kullanıcı profil yöneticisi penceresini açar", @@ -17362,7 +17512,7 @@ "ko_KR": "정보 창 열기", "no_NO": "Åpne Om Vindu", "pl_PL": "Otwórz Okno Informacje", - "pt_BR": "Abrir janela sobre", + "pt_BR": "Abrir Janela Sobre", "ru_RU": "Открывает окно «О программе»", "sv_SE": "Öppna Om-fönstret", "th_TH": "เปิดหน้าต่าง เกี่ยวกับ", @@ -17387,7 +17537,7 @@ "ko_KR": "그리드 크기", "no_NO": "Rutenett størrelse", "pl_PL": "Wielkość siatki", - "pt_BR": "Tamanho da grade", + "pt_BR": "Tamanho da Grade", "ru_RU": "Размер сетки", "sv_SE": "Rutnätsstorlek", "th_TH": "ขนาดตาราง", @@ -17462,7 +17612,7 @@ "ko_KR": "모든 기여자 보기", "no_NO": "Se alle bidragsytere", "pl_PL": "Zobacz Wszystkich Współtwórców", - "pt_BR": "Ver todos os contribuidores", + "pt_BR": "Ver Todos os Contribuidores", "ru_RU": "Посмотреть всех участников", "sv_SE": "Visa alla som bidragit", "th_TH": "ดูผู้มีส่วนร่วมทั้งหมด", @@ -17512,7 +17662,7 @@ "ko_KR": "음량 변경", "no_NO": "Endre lydenivå", "pl_PL": "Zmień Głośność Dźwięku", - "pt_BR": "Mudar volume do áudio", + "pt_BR": "Mudar Volume do Áudio", "ru_RU": "Изменяет громкость звука", "sv_SE": "Ändra ljudvolym", "th_TH": "ปรับระดับเสียง", @@ -17537,7 +17687,7 @@ "ko_KR": "게스트 인터넷 접속/LAN 모드", "no_NO": "Internett-tilgang for gjeste/LAN-modus", "pl_PL": "Dostęp do Internetu Gościa/Tryb LAN", - "pt_BR": "Habilitar acesso à internet do programa convidado", + "pt_BR": "Acesso à Internet para Convidados/Modo LAN", "ru_RU": "Гостевой доступ в интернет/сетевой режим", "sv_SE": "Gäståtkomst för Internet/LAN-läge", "th_TH": "การเข้าถึงอินเทอร์เน็ตของผู้เยี่ยมชม/โหมด LAN", @@ -17562,7 +17712,7 @@ "ko_KR": "에뮬레이트된 앱을 인터넷에 연결할 수 있습니다.\n\nLAN 모드가 있는 게임은 이 기능이 활성화되고 시스템이 동일한 접속 포인트에 연결되어 있을 때 서로 연결할 수 있습니다. 이는 실제 콘솔도 포함됩니다.\n\nNintendo 서버 연결을 허용하지 않습니다. 인터넷에 연결을 시도하는 특정 게임에서 충돌이 발생할 수 있습니다.\n\n모르면 끔으로 두세요.", "no_NO": "Tillater emulert applikasjon å koble til Internett.\n\nSpill med en LAN-modus kan koble til hverandre når dette er aktivert og systemene er koblet til det samme tilgangspunktet. Dette inkluderer ekte konsoller også.\n\ntillater IKKE tilkobling til Nintendo servere. Kan forårsake krasjing i enkelte spill som prøver å koble til Internett.\n\nLa stå AV hvis du er usikker.", "pl_PL": "Pozwala emulowanej aplikacji na łączenie się z Internetem.\n\nGry w trybie LAN mogą łączyć się ze sobą, gdy ta opcja jest włączona, a systemy są połączone z tym samym punktem dostępu. Dotyczy to również prawdziwych konsol.\n\nNie pozwala na łączenie się z serwerami Nintendo. Może powodować awarie niektórych gier, które próbują połączyć się z Internetem.\n\nPozostaw WYŁĄCZONE, jeśli nie masz pewności.", - "pt_BR": "Habilita acesso à internet do programa convidado. Se habilitado, o aplicativo vai se comportar como se o sistema Switch emulado estivesse conectado a Internet. Note que em alguns casos, aplicativos podem acessar a Internet mesmo com essa opção desabilitada", + "pt_BR": "Permite que o aplicativo emulado se conecte à Internet.\n\nJogos com modo LAN podem se conectar uns aos outros quando isso está habilitado e os sistemas estão conectados ao mesmo ponto de acesso. Isso inclui consoles reais também.\n\nNÃO permite a conexão com servidores Nintendo. Pode causar travamentos em certos jogos que tentam se conectar à Internet.\n\nDeixe DESLIGADO se não tiver certeza.", "ru_RU": "Позволяет эмулированному приложению подключаться к Интернету.\n\nПри включении этой функции игры с возможностью сетевой игры могут подключаться друг к другу, если все эмуляторы (или реальные консоли) подключены к одной и той же точке доступа.\n\nНЕ разрешает подключение к серверам Nintendo. Может вызвать сбой в некоторых играх, которые пытаются подключиться к Интернету.\n\nРекомендутеся оставить выключенным.", "sv_SE": "Tillåter det emulerade programmet att ansluta till internet.\n\nSpel med ett LAN-läge kan ansluta till varandra när detta är aktiverat och systemen är anslutna till samma åtkomstpunkt. Detta inkluderar riktiga konsoler också.\n\nTillåter INTE anslutning till Nintendo-servrar. Kan orsaka kraschar i vissa spel som försöker ansluta till internet.\n\nLämna AV om du är osäker.", "th_TH": "อนุญาตให้แอปพลิเคชันจำลองเชื่อมต่ออินเทอร์เน็ต\n\nเกมที่มีโหมด LAN สามารถเชื่อมต่อระหว่างกันได้เมื่อเปิดใช้งานและระบบเชื่อมต่อกับจุดเชื่อมต่อเดียวกัน รวมถึงคอนโซลจริงด้วย\n\nไม่อนุญาตให้มีการเชื่อมต่อกับเซิร์ฟเวอร์ Nintendo อาจทำให้เกิดการหยุดทำงานในบางเกมที่พยายามเชื่อมต่ออินเทอร์เน็ต\n\nปล่อยให้ปิดหากคุณไม่แน่ใจ", @@ -17587,7 +17737,7 @@ "ko_KR": "치트 관리", "no_NO": "Administrer juksemoduser", "pl_PL": "Zarządzaj Kodami", - "pt_BR": "Gerenciar Cheats", + "pt_BR": "Gerenciar Trapaças", "ru_RU": "Открывает окно управления читами", "sv_SE": "Hantera fusk", "th_TH": "ฟังก์ชั่นจัดการสูตรโกง", @@ -17612,7 +17762,7 @@ "ko_KR": "치트 관리", "no_NO": "Administrer juksemoduser", "pl_PL": "Zarządzaj Kodami", - "pt_BR": "Gerenciar Cheats", + "pt_BR": "Gerenciar Trapaças", "ru_RU": "Управление читами", "sv_SE": "Hantera fusk", "th_TH": "ฟังก์ชั่นจัดการสูตรโกง", @@ -17837,7 +17987,7 @@ "ko_KR": "네트워크 연결", "no_NO": "Nettverk tilkobling", "pl_PL": "Połączenie Sieciowe", - "pt_BR": "Conexão de rede", + "pt_BR": "Conexão de Rede", "ru_RU": "Подключение к сети", "sv_SE": "Nätverksanslutning", "th_TH": "การเชื่อมต่อเครือข่าย", @@ -17887,7 +18037,7 @@ "ko_KR": "CPU 모드", "no_NO": "Prosessor modus", "pl_PL": "Pamięć CPU", - "pt_BR": "Memória da CPU", + "pt_BR": "Modo da CPU", "ru_RU": "Режим работы процессора", "sv_SE": "CPU-läge", "th_TH": "โหมดซีพียู", @@ -17912,7 +18062,7 @@ "ko_KR": "업데이터가 비활성화되었습니다!", "no_NO": "Oppdatering Deaktivert!", "pl_PL": "Aktualizator Wyłączony!", - "pt_BR": "Atualizador desabilitado!", + "pt_BR": "Atualizador Desabilitado!", "ru_RU": "Средство обновления отключено", "sv_SE": "Uppdateringar inaktiverade!", "th_TH": "ปิดใช้งานการอัปเดตแล้ว!", @@ -17937,12 +18087,12 @@ "ko_KR": "", "no_NO": "Oppdatering tilgjengelig!", "pl_PL": "", - "pt_BR": "Atualização disponível!", - "ru_RU": "", - "sv_SE": "", + "pt_BR": "Atualização Disponível!", + "ru_RU": "Доступно обновление!", + "sv_SE": "Uppdatering finns tillgänglig!", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Доступне оновлення!", "zh_CN": "有可用的更新!", "zh_TW": "" } @@ -17962,7 +18112,7 @@ "ko_KR": "시계 방향으로 90° 회전", "no_NO": "Roter 90° med klokken", "pl_PL": "Obróć o 90° w Prawo", - "pt_BR": "Rodar 90° sentido horário", + "pt_BR": "Rodar 90° Sentido Horário", "ru_RU": "Повернуть на 90° по часовой стрелке", "sv_SE": "Rotera 90° medurs", "th_TH": "หมุน 90 องศา ตามเข็มนาฬิกา", @@ -17987,7 +18137,7 @@ "ko_KR": "아이콘 크기", "no_NO": "Ikon størrelse", "pl_PL": "Rozmiar ikon", - "pt_BR": "Tamanho do ícone", + "pt_BR": "Tamanho do Ícone", "ru_RU": "Размер обложек", "sv_SE": "Ikonstorlek", "th_TH": "ขนาดไอคอน", @@ -18037,7 +18187,7 @@ "ko_KR": "콘솔 표시", "no_NO": "Vis konsoll", "pl_PL": "Pokaż Konsolę", - "pt_BR": "Exibir console", + "pt_BR": "Exibir Console", "ru_RU": "Показать консоль", "sv_SE": "Visa konsoll", "th_TH": "แสดง คอนโซล", @@ -18062,7 +18212,7 @@ "ko_KR": "{0}에서 셰이더 캐시를 삭제하는 중 오류 발생 : {1}", "no_NO": "Feil under tømming av shader cache ved {0}: {1}", "pl_PL": "Błąd podczas czyszczenia cache shaderów w {0}: {1}", - "pt_BR": "Erro ao deletar o shader em {0}: {1}", + "pt_BR": "Erro ao limpar o cache do shader em {0}: {1}", "ru_RU": "Ошибка очистки кэша шейдеров в {0}: {1}", "sv_SE": "Fel vid tömning av shader cache i {0}: {1}", "th_TH": "เกิดข้อผิดพลาดในการล้างแคชแสงเงา {0}: {1}", @@ -18137,7 +18287,7 @@ "ko_KR": "펌웨어 구문 분석 오류", "no_NO": "Fastvare analysefeil", "pl_PL": "Błąd parsowania firmware'u", - "pt_BR": "Erro na leitura do Firmware", + "pt_BR": "Erro de análise de firmware", "ru_RU": "Ошибка извлечения прошивки", "sv_SE": "Tolkningsfel i firmware", "th_TH": "เกิดข้อผิดพลาดในการวิเคราะห์เฟิร์มแวร์", @@ -18337,7 +18487,7 @@ "ko_KR": "알 수 없는 오류가 발생했습니다!", "no_NO": "En ukjent feil oppstod!", "pl_PL": "Wystąpił nieznany błąd!", - "pt_BR": "Um erro desconhecido foi encontrado!", + "pt_BR": "Ocorreu um erro desconhecido!", "ru_RU": "Произошла неизвестная ошибка", "sv_SE": "Ett okänt fel inträffade!", "th_TH": "เกิดข้อผิดพลาดที่ไม่รู้จัก!", @@ -18387,7 +18537,7 @@ "ko_KR": "설정 가이드 열기", "no_NO": "Åpne oppsettsveiledningen", "pl_PL": "Otwórz Podręcznik Konfiguracji", - "pt_BR": "Abrir o guia de configuração", + "pt_BR": "Abrir o Guia de Configuração", "ru_RU": "Открыть руководство по установке", "sv_SE": "Öppna konfigurationsguiden", "th_TH": "เปิดคู่มือการตั้งค่า", @@ -18412,7 +18562,7 @@ "ko_KR": "업데이트 없음", "no_NO": "Ingen oppdatering", "pl_PL": "Brak Aktualizacji", - "pt_BR": "Sem atualizações", + "pt_BR": "Sem Atualizações", "ru_RU": "Без обновлений", "sv_SE": "Ingen uppdatering", "th_TH": "ไม่มีการอัปเดต", @@ -18537,7 +18687,7 @@ "ko_KR": "트리밍되지 않음", "no_NO": "Ikke trimmet", "pl_PL": "", - "pt_BR": "Não reduzido", + "pt_BR": "Não Reduzido", "ru_RU": "Не обрезан", "sv_SE": "Inte optimerad", "th_TH": "", @@ -18612,7 +18762,7 @@ "ko_KR": "{0:n0} Mb 저장", "no_NO": "Spare {0:n0} Mb", "pl_PL": "", - "pt_BR": "Salvo {0:n0} Mb", + "pt_BR": "Salvar {0:n0} Mb", "ru_RU": "Сохранить {0:n0} Мб", "sv_SE": "Spara {0:n0} Mb", "th_TH": "", @@ -18817,7 +18967,7 @@ "sv_SE": "Cabinet-dialog", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Сповіщення Cabinet", "zh_CN": "档案对话框", "zh_TW": "Cabinet 對話方塊" } @@ -18987,7 +19137,7 @@ "ko_KR": "지원되는 컨트롤러 :", "no_NO": "Støttede kontrollere:", "pl_PL": "Obsługiwane Kontrolery:", - "pt_BR": "Controladores suportados:", + "pt_BR": "Controladores Suportados:", "ru_RU": "Поддерживаемые геймпады:", "sv_SE": "Handkontroller som stöds:", "th_TH": "คอนโทรลเลอร์ที่รองรับ:", @@ -19062,7 +19212,7 @@ "ko_KR": "도킹 모드가 설정되었습니다. 휴대용 제어 기능을 비활성화해야 합니다.", "no_NO": "Docked modus. Håndholdt kontroll skal være deaktivert.", "pl_PL": "Ustawiony tryb zadokowany. Sterowanie przenośne powinno być wyłączone.", - "pt_BR": "Modo ancorado definido. O controle portátil deve ser desabilitado.", + "pt_BR": "Modo TV definido. O controle portátil deve ser desabilitado.", "ru_RU": "Используется стационарный режим. Управление в портативном режиме должно быть отключено.", "sv_SE": "Dockat läge angivet. Handhållna kontroller bör inaktiveras.", "th_TH": "ตั้งค่าด็อกโหมด ควรปิดใช้งานการควบคุมแบบแฮนด์เฮลด์", @@ -19137,7 +19287,7 @@ "ko_KR": "새 파일 추가...", "no_NO": "Legger til nye filer...", "pl_PL": "Dodawanie Nowych Plików...", - "pt_BR": "Adicionando novos arquivos...", + "pt_BR": "Adicionando Novos Arquivos...", "ru_RU": "Добавление новых файлов...", "sv_SE": "Lägger till nya filer...", "th_TH": "กำลังเพิ่มไฟล์ใหม่...", @@ -19162,7 +19312,7 @@ "ko_KR": "업데이트 추출...", "no_NO": "Pakker ut oppdatering...", "pl_PL": "Wypakowywanie Aktualizacji...", - "pt_BR": "Extraíndo atualização...", + "pt_BR": "Extraíndo Atualização...", "ru_RU": "Извлечение обновления...", "sv_SE": "Extraherar uppdatering...", "th_TH": "กำลังแยกการอัปเดต...", @@ -19187,7 +19337,7 @@ "ko_KR": "업데이트 내려받기 중...", "no_NO": "Laster ned oppdatering...", "pl_PL": "Pobieranie Aktualizacji...", - "pt_BR": "Baixando atualização...", + "pt_BR": "Baixando Atualização...", "ru_RU": "Загрузка обновления...", "sv_SE": "Hämtar uppdatering...", "th_TH": "กำลังดาวน์โหลดอัปเดต...", @@ -19212,7 +19362,7 @@ "ko_KR": "도킹", "no_NO": "Forankret", "pl_PL": "Zadokowany", - "pt_BR": "TV", + "pt_BR": "Dock/TV", "ru_RU": "Стационарный режим", "sv_SE": "Dockad", "th_TH": "ด็อก", @@ -19262,7 +19412,7 @@ "ko_KR": "연결 오류가 발생했습니다.", "no_NO": "Tilkoblingsfeil", "pl_PL": "Błąd Połączenia.", - "pt_BR": "Erro de conexão.", + "pt_BR": "Erro de Conexão.", "ru_RU": "Ошибка соединения", "sv_SE": "Anslutningsfel.", "th_TH": "การเชื่อมต่อล้มเหลว", @@ -19412,7 +19562,7 @@ "ko_KR": "모든 키보드", "no_NO": "Alle tastaturer", "pl_PL": "Wszystkie klawiatury", - "pt_BR": "Todos os teclados", + "pt_BR": "Todos os Teclados", "ru_RU": "Все клавиатуры", "sv_SE": "Alla tangentbord", "th_TH": "คีย์บอร์ดทั้งหมด", @@ -19462,7 +19612,7 @@ "ko_KR": "압축 해제된 게임이 있는 폴더를 선택", "no_NO": "Velg en mappe med et pakket ut spill", "pl_PL": "Wybierz folder z rozpakowaną grą", - "pt_BR": "Selecione um diretório com um jogo extraído", + "pt_BR": "Selecione uma pasta com um jogo descompactado", "ru_RU": "Выберите папку с распакованной игрой", "sv_SE": "Välj en mapp med ett uppackat spel", "th_TH": "เลือกโฟลเดอร์ที่มีเกมที่แตกไฟล์แล้ว", @@ -19487,7 +19637,7 @@ "ko_KR": "지원되는 모든 형식", "no_NO": "Alle støttede formater", "pl_PL": "Wszystkie Obsługiwane Formaty", - "pt_BR": "Todos os formatos suportados", + "pt_BR": "Todos os Formatos Suportados", "ru_RU": "Все поддерживаемые форматы", "sv_SE": "Alla format som stöds", "th_TH": "รูปแบบที่รองรับทั้งหมด", @@ -19522,6 +19672,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": { @@ -19537,7 +19712,7 @@ "ko_KR": "키보드 단축키", "no_NO": "Hurtigtaster for tastatur", "pl_PL": "Skróty Klawiszowe Klawiatury", - "pt_BR": "Atalhos do teclado", + "pt_BR": "Atalhos do Teclado", "ru_RU": "Горячие клавиши", "sv_SE": "Snabbtangenter för tangentbord", "th_TH": "ปุ่มลัดของคีย์บอร์ด", @@ -19562,7 +19737,7 @@ "ko_KR": "키보드 단축키", "no_NO": "Hurtigtaster for tastatur", "pl_PL": "Skróty Klawiszowe Klawiatury", - "pt_BR": "Atalhos do teclado", + "pt_BR": "Atalhos do Teclado", "ru_RU": "Горячие клавиши", "sv_SE": "Snabbtangenter för tangentbord", "th_TH": "ปุ่มลัดของคีย์บอร์ด", @@ -19587,7 +19762,7 @@ "ko_KR": "스크린샷 :", "no_NO": "Skjermbilde", "pl_PL": "Zrzut Ekranu:", - "pt_BR": "Captura de tela:", + "pt_BR": "Captura de Tela:", "ru_RU": "Скриншот:", "sv_SE": "Skärmbild:", "th_TH": "ภาพหน้าจอ:", @@ -19687,7 +19862,7 @@ "ko_KR": "모션 컨트롤 설정", "no_NO": "Innstillinger for bevegelses kontroll", "pl_PL": "Ustawienia Sterowania Ruchowego", - "pt_BR": "Configurações do controle de movimento", + "pt_BR": "Configurações do Controle de Movimento", "ru_RU": "Настройки управления движением", "sv_SE": "Inställningar för rörelsekontroller", "th_TH": "ตั้งค่าควบคุมการเคลื่อนไหว", @@ -19712,7 +19887,7 @@ "ko_KR": "진동 설정", "no_NO": "Innstillinger for Vibrasjon", "pl_PL": "Ustawienia Wibracji", - "pt_BR": "Configurações de vibração", + "pt_BR": "Configurações de Vibração", "ru_RU": "Настройки вибрации", "sv_SE": "Inställningar för vibration", "th_TH": "ตั้งค่าการสั่นไหว", @@ -19738,11 +19913,11 @@ "no_NO": "LED-innstillinger", "pl_PL": "", "pt_BR": "Configurações de LED", - "ru_RU": "", + "ru_RU": "Настройки LED-подсветки", "sv_SE": "LED-inställningar", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Налаштування LED", "zh_CN": "LED 设置", "zh_TW": "" } @@ -19762,7 +19937,7 @@ "ko_KR": "테마 파일 선택", "no_NO": "Velg tema fil", "pl_PL": "Wybierz Plik Motywu", - "pt_BR": "Selecionar arquivo do tema", + "pt_BR": "Selecionar Arquivo de Tema", "ru_RU": "Выбрать файл темы", "sv_SE": "Välj temafil", "th_TH": "เลือกธีมไฟล์", @@ -19787,7 +19962,7 @@ "ko_KR": "Xaml 테마 파일", "no_NO": "Xaml tema-fil", "pl_PL": "Plik Motywu Xaml", - "pt_BR": "Arquivo de tema Xaml", + "pt_BR": "Arquivo de Tema Xaml", "ru_RU": "Файл темы Xaml", "sv_SE": "Xaml-temafil", "th_TH": "ไฟล์ธีมรูปแบบ XAML", @@ -19812,7 +19987,7 @@ "ko_KR": "계정 관리 - 아바타", "no_NO": "Administrer kontoer - Profilbilde", "pl_PL": "Zarządzaj Kontami — Avatar", - "pt_BR": "Gerenciar contas - Avatar", + "pt_BR": "Gerenciar Contas - Avatar", "ru_RU": "Управление аккаунтами - Аватар", "sv_SE": "Hantera konton - Avatar", "th_TH": "จัดการบัญชี - อวาต้า", @@ -19838,7 +20013,7 @@ "no_NO": "", "pl_PL": "", "pt_BR": "", - "ru_RU": "", + "ru_RU": "Амибо", "sv_SE": "", "th_TH": "", "tr_TR": "", @@ -19937,7 +20112,7 @@ "ko_KR": "DLC 파일 선택", "no_NO": "Velg DLC-filer", "pl_PL": "Wybierz pliki DLC", - "pt_BR": "Selecionar arquivos de DLC", + "pt_BR": "Selecionar Arquivos de DLC", "ru_RU": "Выберите файлы DLC", "sv_SE": "Välj DLC-filer", "th_TH": "เลือกไฟล์ DLC", @@ -20012,7 +20187,7 @@ "ko_KR": "XCI 파일 확인 및 정리", "no_NO": "Kontroller og trim XCI-filen", "pl_PL": "", - "pt_BR": "Verifique e reduza o arquivo XCI", + "pt_BR": "Verifique e Reduza o Arquivo XCI", "ru_RU": "Проверить и обрезать XCI файл", "sv_SE": "Kontrollera och optimera XCI-filer", "th_TH": "", @@ -20112,7 +20287,7 @@ "ko_KR": "XCI 파일은 트리밍을 해제할 수 없습니다. 자세한 내용은 로그를 확인", "no_NO": "XCI-filen kan ikke trimmes. Sjekk loggene for mer informasjon", "pl_PL": "", - "pt_BR": "O arquivo XCI não pode ser desfeito. Verifique os logs para mais detalhes", + "pt_BR": "O arquivo XCI reduzido não pode ser desfeito. Verifique os logs para mais detalhes", "ru_RU": "XCI файл не может быть обрезан. Проверьте логи для получения более подробной информации", "sv_SE": "XCI-filen kan inte avoptimeras. Kontrollera loggen för mer information", "th_TH": "", @@ -20337,8 +20512,8 @@ "ko_KR": "사용자 프로필 관리자", "no_NO": "Bruker Profiler Behandler", "pl_PL": "Menedżer Profili Użytkowników", - "pt_BR": "Gerenciador de perfis de usuário", - "ru_RU": "Менеджер учетных записей", + "pt_BR": "Gerenciador de Perfis de Usuário", + "ru_RU": "Менеджер учётных записей", "sv_SE": "Hanterare för användarprofiler", "th_TH": "จัดการโปรไฟล์ผู้ใช้", "tr_TR": "Kullanıcı Profillerini Yönet", @@ -20362,7 +20537,7 @@ "ko_KR": "치트 관리자", "no_NO": "Juksing behandler", "pl_PL": "Menedżer Kodów", - "pt_BR": "Gerenciador de Cheats", + "pt_BR": "Gerenciador de Trapaças", "ru_RU": "Менеджер читов", "sv_SE": "Fuskhanterare", "th_TH": "จัดการสูตรโกง", @@ -20387,7 +20562,7 @@ "ko_KR": "{0} ({1})의 내려받기 가능한 콘텐츠 관리", "no_NO": "Behandle nedlastbart innhold for {0} ({1})", "pl_PL": "Menedżer Zawartości do Pobrania", - "pt_BR": "Gerenciador de DLC", + "pt_BR": "Gerenciar conteúdo para download para {0} ({1})", "ru_RU": "Управление DLC для {0} ({1})", "sv_SE": "Hantera hämtningsbart innehåll för {0} ({1})", "th_TH": "จัดการ DLC ที่ดาวน์โหลดได้สำหรับ {0} ({1})", @@ -20437,7 +20612,7 @@ "ko_KR": "타이틀 업데이트 관리자", "no_NO": "Tittel oppdatering behandler", "pl_PL": "Menedżer Aktualizacji Tytułu", - "pt_BR": "Gerenciador de atualizações", + "pt_BR": "Gerenciador de Atualização de Título", "ru_RU": "Менеджер обновлений игр", "sv_SE": "Hanterare för speluppdateringar", "th_TH": "จัดการอัปเดตหัวข้อ", @@ -20462,7 +20637,7 @@ "ko_KR": "XCI 파일 트리머", "no_NO": "", "pl_PL": "", - "pt_BR": "Redutor de arquivo XCI", + "pt_BR": "Redutor de Arquivo XCI", "ru_RU": "Уменьшение размера XCI файлов", "sv_SE": "Optimera XCI-filer", "th_TH": "", @@ -20562,7 +20737,7 @@ "ko_KR": "{0}개의 타이틀을 트리밍 해제 중...", "no_NO": "Untrimming {0} Tittel(er)...", "pl_PL": "", - "pt_BR": "Desfazendo {0} Título(s)...", + "pt_BR": "Desfazendo redução {0} Título(s)...", "ru_RU": "Отмена обрезки {0} файла(ов)...", "sv_SE": "Avoptimerar {0} spel...", "th_TH": "", @@ -20612,7 +20787,7 @@ "ko_KR": "잠재적 비용 절감", "no_NO": "Potensielle besparelser", "pl_PL": "", - "pt_BR": "Economia potencial", + "pt_BR": "Economia Potencial", "ru_RU": "Потенциально освобождено места", "sv_SE": "Möjlig besparning", "th_TH": "", @@ -20637,7 +20812,7 @@ "ko_KR": "실제 비용 절감", "no_NO": "Faktiske besparelser", "pl_PL": "", - "pt_BR": "Economia real", + "pt_BR": "Economia Real", "ru_RU": "Реально освобождено места", "sv_SE": "Faktisk besparning", "th_TH": "", @@ -20687,12 +20862,12 @@ "ko_KR": "표시됨 선택", "no_NO": "Velg vist", "pl_PL": "", - "pt_BR": "Selecionar mostrado(s)", + "pt_BR": "Marcar Todos", "ru_RU": "Выбрать то что показано", "sv_SE": "Markera visade", "th_TH": "", "tr_TR": "", - "uk_UA": "Вибрати показане", + "uk_UA": "Вибрати показані", "zh_CN": "选定显示的", "zh_TW": "選擇已顯示" } @@ -20712,7 +20887,7 @@ "ko_KR": "표시됨 선택 취소", "no_NO": "Opphev valg av Vist", "pl_PL": "", - "pt_BR": "Desmarcar mostrado(s)", + "pt_BR": "Desmarcar Todos", "ru_RU": "Отменить выбор показанного", "sv_SE": "Avmarkera visade", "th_TH": "", @@ -20762,7 +20937,7 @@ "ko_KR": "공간 절약s", "no_NO": "Plassbesparelser", "pl_PL": "", - "pt_BR": "Economia de espaço", + "pt_BR": "Economia de Espaço", "ru_RU": "Сохранение места на диске", "sv_SE": "Utrymmesbesparning", "th_TH": "", @@ -20787,7 +20962,7 @@ "ko_KR": "트림", "no_NO": "", "pl_PL": "", - "pt_BR": "Reduzir", + "pt_BR": "Reduzir XCI", "ru_RU": "Обрезать", "sv_SE": "Optimera", "th_TH": "", @@ -20812,7 +20987,7 @@ "ko_KR": "언트림", "no_NO": "Utrim", "pl_PL": "", - "pt_BR": "Desfazer", + "pt_BR": "Desfazer Redução", "ru_RU": "Отмена обрезки", "sv_SE": "Avoptimera", "th_TH": "", @@ -20887,7 +21062,7 @@ "ko_KR": "{0} [{1}]에 사용 가능한 치트", "no_NO": "Juks tilgjengelig for {0} [{1}]", "pl_PL": "Kody Dostępne dla {0} [{1}]", - "pt_BR": "Cheats disponíveis para {0} [{1}]", + "pt_BR": "Trapaças disponíveis para {0} [{1}]", "ru_RU": "Доступные читы для {0} [{1}]", "sv_SE": "Fusk tillgängliga för {0} [{1}]", "th_TH": "สูตรโกงมีให้สำหรับ {0} [{1}]", @@ -21262,7 +21437,7 @@ "ko_KR": "일시 중지됨", "no_NO": "Satt på pause", "pl_PL": "Wstrzymano", - "pt_BR": "", + "pt_BR": "Pausado", "ru_RU": "Приостановлено", "sv_SE": "Pausad", "th_TH": "หยุดชั่วคราว", @@ -21287,7 +21462,7 @@ "ko_KR": "프로필 이미지 설정", "no_NO": "Angi profilbilde", "pl_PL": "Ustaw Obraz Profilu", - "pt_BR": "Definir imagem de perfil", + "pt_BR": "Definir Imagem de Perfil", "ru_RU": "Установить аватар", "sv_SE": "Välj profilbild", "th_TH": "ตั้งค่ารูปโปรไฟล์", @@ -21312,7 +21487,7 @@ "ko_KR": "이름 필수 입력", "no_NO": "Navn er påkrevd", "pl_PL": "Nazwa jest wymagana", - "pt_BR": "É necessário um nome", + "pt_BR": "Nome é obrigatório", "ru_RU": "Необходимо ввести никнейм", "sv_SE": "Namn krävs", "th_TH": "จำเป็นต้องระบุชื่อ", @@ -21337,7 +21512,7 @@ "ko_KR": "프로필 이미지를 설정해야 함", "no_NO": "Profilbilde må være angitt", "pl_PL": "Należy ustawić obraz profilowy", - "pt_BR": "A imagem de perfil deve ser definida", + "pt_BR": "A Imagem de Perfil Deve ser Definida", "ru_RU": "Необходимо установить аватар", "sv_SE": "Profilbild måste anges", "th_TH": "จำเป็นต้องตั้งค่ารูปโปรไฟล์", @@ -21387,7 +21562,7 @@ "ko_KR": "해상도 증가 :", "no_NO": "Øke oppløsning:", "pl_PL": "Zwiększ Rozdzielczość:", - "pt_BR": "Aumentar a resolução:", + "pt_BR": "Aumentar a Resolução:", "ru_RU": "Увеличить разрешение:", "sv_SE": "Öka upplösning:", "th_TH": "เพิ่มความละเอียด:", @@ -21412,7 +21587,7 @@ "ko_KR": "해상도 감소 :", "no_NO": "Reduser oppløsning:", "pl_PL": "Zmniejsz Rozdzielczość:", - "pt_BR": "Diminuir a resolução:", + "pt_BR": "Reduzir a Resolução:", "ru_RU": "Уменьшить разрешение:", "sv_SE": "Sänk upplösning:", "th_TH": "ลดความละเอียด:", @@ -21462,7 +21637,7 @@ "ko_KR": "사용자 ID :", "no_NO": "Bruker ID:", "pl_PL": "ID Użytkownika:", - "pt_BR": "ID de usuário:", + "pt_BR": "ID de Usuário:", "ru_RU": "ID пользователя:", "sv_SE": "Användar-id:", "th_TH": "รหัสผู้ใช้:", @@ -21487,7 +21662,7 @@ "ko_KR": "그래픽 후단부", "no_NO": "Grafikk Backend", "pl_PL": "Backend Graficzny", - "pt_BR": "Backend gráfico", + "pt_BR": "Renderizador Gráfico", "ru_RU": "Графический бэкенд", "sv_SE": "Grafikbakände", "th_TH": "กราฟิกเบื้องหลัง", @@ -21512,7 +21687,7 @@ "ko_KR": "에뮬레이터에서 사용할 그래픽 후단부를 선택합니다.\n\nVulkan은 드라이버가 최신 상태인 한 모든 최신 그래픽 카드에 전반적으로 더 좋습니다. Vulkan은 또한 모든 GPU 공급업체에서 더 빠른 셰이더 컴파일(덜 끊김)을 제공합니다.\n\nOpenGL은 오래된 Nvidia GPU, Linux의 오래된 AMD GPU 또는 VRAM이 낮은 GPU에서 더 나은 결과를 얻을 수 있지만 셰이더 컴파일 끊김이 더 큽니다.\n\n모르면 Vulkan으로 설정합니다. 최신 그래픽 드라이버를 사용해도 GPU가 Vulkan을 지원하지 않는 경우 OpenGL로 설정하세요..", "no_NO": "Velg grafikkbackend som skal brukes i emulatoren.\n\nVulkan er generelt bedre for alle moderne grafikkort, så lenge driverne er oppdatert. Vulkan har også en raskere sammenstilling av Shader (mindre hakkete) på alle GPU-leverandører.\n\nOpenGL kan oppnå bedre resultater for eldre Nvidia GPU-er, på eldre AMD GPU-er på Linux, eller på GPU-er med lavere VRAM, selv om skyggekompileringsutløser vil være større.\n\nSett til Vulkan hvis du er usikker. Sett til OpenGL hvis ikke GPU-en støtter Vulkan selv med de nyeste grafikkdriverne.", "pl_PL": "", - "pt_BR": "Selecione o backend gráfico que será usado no emulador.\n\nO Vulkan é melhor no geral para todas as placas de vídeo modernas, desde que seus drivers estejam atualizados. O Vulkan também apresenta compilação de shader mais rápida (menos travamentos) em todos os fornecedores de GPU.\n\nO OpenGL pode obter melhores resultados em GPUs Nvidia antigas, em GPUs AMD antigas no Linux ou em GPUs com VRAM menor, embora os travamentos de compilação de shader sejam maiores.\n\nDefina como Vulkan se não tiver certeza. Defina como OpenGL se sua GPU não suportar Vulkan, mesmo com os drivers gráficos mais recentes.", + "pt_BR": "Selecione o renderizador gráfico que será usado no emulador.\n\nO Vulkan é melhor no geral para todas as placas de vídeo modernas, desde que seus drivers estejam atualizados. O Vulkan também apresenta compilação de shader mais rápida (menos travamentos) em todos os fornecedores de GPU.\n\nO OpenGL pode obter melhores resultados em GPUs Nvidia antigas, em GPUs AMD antigas no Linux ou em GPUs com VRAM menor, embora os travamentos de compilação de shader sejam maiores.\n\nDefina como Vulkan se não tiver certeza. Defina como OpenGL se sua GPU não suportar Vulkan, mesmo com os drivers gráficos mais recentes.", "ru_RU": "Выбирает бэкенд, который будет использован в эмуляторе.\n\nVulkan является лучшим выбором для всех современных графических карт с актуальными драйверами. В Vulkan также включена более быстрая компиляция шейдеров (меньше статтеров) для всех видеоадаптеров.\n\nПри использовании OpenGL можно достичь лучших результатов на старых видеоадаптерах Nvidia и AMD в Linux или на видеоадаптерах с небольшим количеством видеопамяти, хотя статтеров при компиляции шейдеров будет больше.\n\nРекомендуется использовать Vulkan. Используйте OpenGL, если ваш видеоадаптер не поддерживает Vulkan даже с актуальными драйверами.", "sv_SE": "Väljer den grafikbakände som ska användas i emulatorn.\n\nVulkan är oftast bättre för alla moderna grafikkort, så länge som deras drivrutiner är uppdaterade. Vulkan har också funktioner för snabbare shader compilation (mindre stuttering) för alla GPU-tillverkare.\n\nOpenGL kan nå bättre resultat på gamla Nvidia GPU:er, på äldre AMD GPU:er på Linux, eller på GPU:er med lägre VRAM, även om shader compilation stuttering kommer att vara större.\n\nStäll in till Vulkan om du är osäker. Ställ in till OpenGL om du GPU inte har stöd för Vulkan även med de senaste grafikdrivrutinerna.", "th_TH": "เลือกกราฟิกเบื้องหลังที่จะใช้ในโปรแกรมจำลอง\n\nโดยรวมแล้ว Vulkan นั้นดีกว่าสำหรับการ์ดจอรุ่นใหม่ทั้งหมด ตราบใดที่ไดรเวอร์ยังอัพเดทอยู่เสมอ Vulkan ยังมีคุณสมบัติการคอมไพล์เชเดอร์ที่เร็วขึ้น(และลดอาการกระตุก) สำหรับ GPU อื่นๆทุกอัน\n\nOpenGL อาจได้รับผลลัพธ์ที่ดีกว่าบน Nvidia GPU รุ่นเก่า, AMD GPU รุ่นเก่าบน Linux หรือบน GPU ที่มี VRAM น้อย แม้ว่าการคอมไพล์เชเดอร์ จะทำให้อาการกระตุกมากขึ้นก็ตาม\n\nตั้งค่าเป็น Vulkan หากไม่แน่ใจ ตั้งค่าเป็น OpenGL หาก GPU ของคุณไม่รองรับ Vulkan แม้จะมีไดรเวอร์กราฟิกล่าสุดก็ตาม", @@ -21537,12 +21712,12 @@ "ko_KR": "자동", "no_NO": "", "pl_PL": "", - "pt_BR": "", + "pt_BR": "Automático", "ru_RU": "Авто", "sv_SE": "Automatiskt", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Автоматично", "zh_CN": "自动", "zh_TW": "自動" } @@ -21562,12 +21737,12 @@ "ko_KR": "Vulkan을 사용합니다.\nARM 맥에서 해당 플랫폼에서 잘 실행되는 게임을 플레이하는 경우 Metal 후단부를 사용합니다.", "no_NO": "Bruker Vulkan \nPå en ARM Mac, og når du spiller et spill som kjører bra under den, bruker du Metal-backend.", "pl_PL": "", - "pt_BR": "Usa Vulkan.\nEm um Mac ARM, e ao jogar um jogo que roda bem nele, usa o backend Metal.", + "pt_BR": "Usa Vulkan.\nEm um Mac ARM, e ao jogar um jogo que roda bem nele, usa o renderizador Metal.", "ru_RU": "Использует Vulkan.\nНа Mac с ARM процессорами используется Metal, если игра с ним совместима и хорошо работает.", "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 後端。" } @@ -21587,7 +21762,7 @@ "ko_KR": "텍스처 재압축 활성화", "no_NO": "Aktiver teksturkomprimering", "pl_PL": "Włącz Rekompresję Tekstur", - "pt_BR": "Habilitar recompressão de texturas", + "pt_BR": "Habilitar Recompressão de Texturas", "ru_RU": "Пережимать текстуры", "sv_SE": "Aktivera Texture Recompression", "th_TH": "เปิดใช้งาน การบีบอัดพื้นผิวอีกครั้ง", @@ -21637,7 +21812,7 @@ "ko_KR": "기본 GPU", "no_NO": "Foretrukket GPU", "pl_PL": "Preferowane GPU", - "pt_BR": "GPU preferencial", + "pt_BR": "GPU Preferida", "ru_RU": "Предпочтительный видеоадаптер", "sv_SE": "Föredragen GPU", "th_TH": "GPU ที่ต้องการ", @@ -21662,8 +21837,8 @@ "ko_KR": "Vulkan 그래픽 후단부와 함께 사용할 그래픽 카드를 선택하세요.\n\nOpenGL에서 사용할 GPU에는 영향을 미치지 않습니다.\n\n모르면 \"dGPU\"로 플래그가 지정된 GPU로 설정하세요. 없으면 그대로 두세요.", "no_NO": "Velg grafikkkortet som skal brukes sammen med Vulkan grafikkbackenden\n\nPåvirker ikke GPU-er som OpenGL skal bruke.\n\nSett til GPU-merket som \"dGPU\" hvis usikker. Hvis det ikke det er en, la være uberørt.", "pl_PL": "Wybierz kartę graficzną, która będzie używana z backendem graficznym Vulkan.\n\nNie wpływa na GPU używane przez OpenGL.\n\nW razie wątpliwości ustaw flagę GPU jako \"dGPU\". Jeśli żadnej nie ma, pozostaw nietknięte.", - "pt_BR": "Selecione a placa de vídeo que será usada com o backend gráfico Vulkan.\n\nNão afeta a GPU que OpenGL usará.\n\nSelecione \"dGPU\" em caso de dúvida. Se não houver nenhuma, não mexa.", - "ru_RU": "Выберает видеоадаптер, который будет использоваться графическим бэкендом Vulkan.\n\nЭта настройка не влияет на видеоадаптер, который будет использоваться с OpenGL.\n\nЕсли вы не уверены что нужно выбрать, используйте графический процессор, помеченный как \"dGPU\". Если его нет, оставьте выбор по умолчанию.", + "pt_BR": "Selecione a placa de vídeo que será usada com o renderizador gráfico Vulkan.\n\nNão afeta a GPU que OpenGL usará.\n\nSelecione \"dGPU\" em caso de dúvida. Se não houver nenhuma, não mexa.", + "ru_RU": "Выберает видеоадаптер, который будет использоваться графическим бэкендом Vulkan.\n\nЭта настройка не влияет на видеоадаптер, который будет использоваться с OpenGL.\n\nЕсли вы не уверены что нужно выбрать, используйте графический процессор, помеченный как «dGPU». Если его нет, оставьте выбор по умолчанию.", "sv_SE": "Välj grafikkortet som ska användas med Vulkan-grafikbakänden.\n\nPåverkar inte GPU:n som OpenGL använder.\n\nStäll in till den GPU som flaggats som \"dGPU\" om osäker. Om det inte finns någon, lämna orörd.", "th_TH": "เลือกการ์ดจอที่จะใช้กับแบ็กเอนด์กราฟิก Vulkan\n\nไม่ส่งผลต่อ GPU ที่ OpenGL จะใช้\n\nตั้งค่าเป็น GPU ที่ถูกตั้งค่าสถานะเป็น \"dGPU\" ถ้าหากคุณไม่แน่ใจ ,หากไม่มีก็ปล่อยทิ้งไว้โดยไม่ต้องแตะต้องมัน", "tr_TR": "Vulkan Grafik Arka Ucu ile kullanılacak Ekran Kartını Seçin.\n\nOpenGL'nin kullanacağı GPU'yu etkilemez.\n\n Emin değilseniz \"dGPU\" olarak işaretlenmiş GPU'ya ayarlayın. Eğer yoksa, dokunmadan bırakın.\n", @@ -21687,7 +21862,7 @@ "ko_KR": "Ryujinx 다시 시작 필요", "no_NO": "Ryujinx Omstart nødvendig", "pl_PL": "Wymagane Zrestartowanie Ryujinx", - "pt_BR": "Reinicialização do Ryujinx necessária", + "pt_BR": "Reinicialização do Ryujinx Necessária", "ru_RU": "Требуется перезапуск Ryujinx", "sv_SE": "Omstart av Ryujinx krävs", "th_TH": "จำเป็นต้องรีสตาร์ท Ryujinx", @@ -21712,7 +21887,7 @@ "ko_KR": "그래픽 후단부 또는 GPU 설정이 수정되었습니다. 이를 적용하려면 다시 시작이 필요", "no_NO": "Grafikk Backend eller GPU-innstillinger er endret. Dette krever en omstart for å aktiveres", "pl_PL": "Zmieniono ustawienia Backendu Graficznego lub GPU. Będzie to wymagało ponownego uruchomienia", - "pt_BR": "Configurações do backend gráfico ou da GPU foram alteradas. Uma reinicialização é necessária para que as mudanças tenham efeito.", + "pt_BR": "Configurações do renderizador gráfico ou da GPU foram alteradas. Uma reinicialização é necessária para que as mudanças tenham efeito.", "ru_RU": "Графический бэкенд или настройки графического процессора были изменены. Требуется перезапуск для вступления в силу изменений.", "sv_SE": "Grafikbakänden eller GPU-inställningar har ändrats. Detta kräver en omstart", "th_TH": "การตั้งค่ากราฟิกเบื้องหลังหรือ GPU ได้รับการแก้ไขแล้ว สิ่งนี้จะต้องมีการรีสตาร์ทจึงจะสามารถใช้งานได้", @@ -21787,7 +21962,7 @@ "ko_KR": "음량 증가 :", "no_NO": "Øk Volum:", "pl_PL": "Zwiększ Głośność:", - "pt_BR": "Aumentar volume:", + "pt_BR": "Aumentar Volume:", "ru_RU": "Увеличить громкость:", "sv_SE": "Öka volym:", "th_TH": "เพิ่มระดับเสียง:", @@ -21812,7 +21987,7 @@ "ko_KR": "음량 감소 :", "no_NO": "Reduser Volum:", "pl_PL": "Zmniejsz Głośność:", - "pt_BR": "Diminuir volume:", + "pt_BR": "Diminuir Volume:", "ru_RU": "Уменьшить громкость:", "sv_SE": "Sänk volym:", "th_TH": "ลดระดับเสียง:", @@ -21837,7 +22012,7 @@ "ko_KR": "매크로 HLE 활성화", "no_NO": "Aktiver Makro HLE", "pl_PL": "Włącz Macro HLE", - "pt_BR": "Habilitar emulação de alto nível para Macros", + "pt_BR": "Habilitar Macro HLE", "ru_RU": "Использовать макрос высокоуровневой эмуляции видеоадаптера", "sv_SE": "Aktivera Macro HLE", "th_TH": "เปิดใช้งาน มาโคร HLE", @@ -21887,7 +22062,7 @@ "ko_KR": "색 공간 통과", "no_NO": "Fargeromsgjennomgang", "pl_PL": "Przekazywanie przestrzeni kolorów", - "pt_BR": "Passagem de Espaço Cor", + "pt_BR": "Passagem do Espaço de Cores", "ru_RU": "Пропускать цветовое пространство", "sv_SE": "Genomströmning av färgrymd", "th_TH": "ทะลุผ่านพื้นที่สี", @@ -21912,7 +22087,7 @@ "ko_KR": "Vulkan 후단부가 색 공간을 지정하지 않고 색상 정보를 전달하도록 지시합니다. 넓은 색역 화면 표시 장치를 사용하는 사용자의 경우 색상 정확성을 희생하고 더 생생한 색상이 나올 수 있습니다.", "no_NO": "Dirigerer Vulkan backenden til å gå gjennom farge informasjonen uten og spesifisere en fargeromsgjennomgang. For brukere med en bred spillvisning kan dette resultere i mer vibrerende farger og få riktig farge.", "pl_PL": "Nakazuje API Vulkan przekazywać informacje o kolorze bez określania przestrzeni kolorów. Dla użytkowników z wyświetlaczami o szerokim zakresie kolorów może to skutkować bardziej żywymi kolorami, kosztem ich poprawności.", - "pt_BR": "Direciona o backend Vulkan para passar informações de cores sem especificar um espaço de cores. Para usuários com telas de ampla gama, isso pode resultar em cores mais vibrantes, ao custo da correção de cores.", + "pt_BR": "Direciona o renderizador Vulkan para passar informações de cores sem especificar um espaço de cores. Para usuários com telas de ampla gama, isso pode resultar em cores mais vibrantes, ao custo da correção de cores.", "ru_RU": "Направляет бэкенд Vulkan на передачу информации о цвете без указания цветового пространства. Для пользователей с экранами с расширенной гаммой данная настройка приводит к получению более ярких цветов за счет снижения корректности цветопередачи.", "sv_SE": "Dirigerar Vulkan-bakänden att skicka genom färginformation utan att ange en färgrymd. För användare med breda gamut-skärmar kan detta resultera i mer levande färger på bekostnad av färgkorrekthet.", "th_TH": "สั่งให้แบ็กเอนด์ Vulkan ส่งผ่านข้อมูลสีโดยไม่ต้องระบุค่าของสี สำหรับผู้ใช้ที่มีการแสดงกระจายตัวของสี อาจส่งผลให้สีสดใสมากขึ้น โดยต้องแลกกับความถูกต้องของสี", @@ -21937,7 +22112,7 @@ "ko_KR": "음량", "no_NO": "", "pl_PL": "Głoś", - "pt_BR": "", + "pt_BR": "Volume", "ru_RU": "Громкость", "sv_SE": "Volym", "th_TH": "ระดับเสียง", @@ -21962,7 +22137,7 @@ "ko_KR": "저장 관리", "no_NO": "Administrer lagring", "pl_PL": "Zarządzaj Zapisami", - "pt_BR": "Gerenciar jogos salvos", + "pt_BR": "Gerenciar Jogos Salvos", "ru_RU": "Управление сохранениями", "sv_SE": "Hantera sparade spel", "th_TH": "จัดการบันทึก", @@ -22037,7 +22212,7 @@ "ko_KR": "{0} ({1})에 대한 저장 관리", "no_NO": "Administrer lagring for {0} ({1})", "pl_PL": "Zarządzaj Zapisami dla {0}", - "pt_BR": "Gerenciar jogos salvos para {0}", + "pt_BR": "Gerenciar Jogos Salvos para {0}", "ru_RU": "Редактирование сохранений для {0} ({1})", "sv_SE": "Hantera sparade spel för {0} ({1})", "th_TH": "จัดการบันทึกสำหรับ {0} ({1})", @@ -22062,7 +22237,7 @@ "ko_KR": "관리자 저장", "no_NO": "Lagre behandler", "pl_PL": "Menedżer Zapisów", - "pt_BR": "Gerenciador de jogos salvos", + "pt_BR": "Gerenciador de Jogos Salvos", "ru_RU": "Менеджер сохранений", "sv_SE": "Sparhanterare", "th_TH": "จัดการบันทึก", @@ -22162,8 +22337,8 @@ "ko_KR": "잃어버린 계정 복구", "no_NO": "Gjenopprett tapte kontoer", "pl_PL": "Odzyskaj Utracone Konta", - "pt_BR": "Recuperar contas perdidas", - "ru_RU": "Восстановить учетные записи", + "pt_BR": "Recuperar Contas Perdidas", + "ru_RU": "Восстановить учётные записи", "sv_SE": "Återskapa förlorade konton", "th_TH": "กู้คืนบัญชีที่สูญหาย", "tr_TR": "Kayıp Hesapları Kurtar", @@ -22238,7 +22413,7 @@ "no_NO": "Ingen profiler å gjenopprette", "pl_PL": "Brak profili do odzyskania", "pt_BR": "Nenhum perfil para recuperar", - "ru_RU": "Нет учетных записей для восстановления", + "ru_RU": "Нет учётных записей для восстановления", "sv_SE": "Inga profiler att återskapa", "th_TH": "ไม่มีโปรไฟล์ที่สามารถกู้คืนได้", "tr_TR": "Kurtarılacak profil bulunamadı", @@ -22263,7 +22438,7 @@ "no_NO": "Aktiverer anti-aliasing til spill render.\n\nFXAA vil gjøre det meste av bildet, mens SMAA vil forsøke å finne berørte kanter og glatte dem ut.\n\nAnbefales ikke til bruk i forbindelse med FSR-skaleringsfilteret.\n\nDette valget kan endres mens et spill kjører ved å klikke \"Apply\" nedenfor; du kan bare flytte innstillingsvinduet til du finner det foretrukne utseendet til et spill.\n\nForlat på NONE hvis usikker.", "pl_PL": "", "pt_BR": "Aplica anti-aliasing à renderização do jogo.\n\nFXAA borrará a maior parte da imagem, enquanto SMAA tentará identificar e suavizar bordas serrilhadas.\n\nNão é recomendado usar em conjunto com o filtro de escala FSR.\n\nEssa opção pode ser alterada enquanto o jogo está em execução clicando em \"Aplicar\" abaixo; basta mover a janela de configurações para o lado e experimentar até encontrar o visual preferido para o jogo.\n\nDeixe em NENHUM se estiver em dúvida.", - "ru_RU": "Применимое сглаживание для рендера.\n\nFXAA размывает большую часть изображения, SMAA попытается найти \"зазубренные\" края и сгладить их.\n\nНе рекомендуется использовать вместе с масштабирующим фильтром FSR.\n\nЭта опция может быть изменена во время игры по нажатию \"Применить\" ниже; \nВы можете просто переместить окно настроек в сторону и поэкспериментировать, пока не найдёте подходящую настройку игры.\n\nРекомендуется использовать \"Нет\".", + "ru_RU": "Применимое сглаживание для рендера.\n\nFXAA размывает большую часть изображения, SMAA попытается найти «зазубренные» края и сгладить их.\n\nНе рекомендуется использовать вместе с масштабирующим фильтром FSR.\n\nЭта опция может быть изменена во время игры по нажатию «Применить» ниже; \nВы можете просто переместить окно настроек в сторону и поэкспериментировать, пока не найдёте подходящую настройку игры.\n\nРекомендуется использовать «Нет».", "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": "", @@ -22287,7 +22462,7 @@ "ko_KR": "앤티 앨리어싱 :", "no_NO": "Kantutjevning:", "pl_PL": "Antyaliasing:", - "pt_BR": "Anti-serrilhado:", + "pt_BR": "Anti-Serrilhado:", "ru_RU": "Сглаживание:", "sv_SE": "Antialiasing:", "th_TH": "ลดการฉีกขาดของภาพ:", @@ -22312,7 +22487,7 @@ "ko_KR": "크기 조정 필터 :", "no_NO": "Skaleringsfilter:", "pl_PL": "Filtr skalowania:", - "pt_BR": "Filtro de escala:", + "pt_BR": "Filtro de Escala:", "ru_RU": "Интерполяция:", "sv_SE": "Skalningsfilter:", "th_TH": "ปรับขนาดตัวกรอง:", @@ -22338,7 +22513,7 @@ "no_NO": "Velg det skaleringsfilteret som skal brukes når du bruker oppløsningsskalaen.\n\nBilinear fungerer godt for 3D-spill og er et trygt standardalternativ.\n\nNærmeste anbefales for pixel kunst-spill.\n\nFSR 1.0 er bare et skarpere filter, ikke anbefalt for bruk med FXAA eller SMAA.\n\nOmrådeskalering anbefales når nedskalering er større enn utgangsvinduet. Den kan brukes til å oppnå en superprøvetaket anti-aliasingseffekt når en nedskalerer med mer enn 2x.\n\nDette valget kan endres mens et spill kjører ved å klikke \"Apply\" nedenfor; du kan bare flytte innstillingsvinduet til du finner det foretrukne utseendet til et spill.\n\nLa være på BILINEAR hvis usikker.", "pl_PL": "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.", "pt_BR": "Escolha o filtro de escala que será aplicado ao usar a escala de resolução.\n\nBilinear funciona bem para jogos 3D e é uma opção padrão segura.\n\nNearest é recomendado para jogos em pixel art.\n\nFSR 1.0 é apenas um filtro de nitidez, não recomendado para uso com FXAA ou SMAA.\n\nEssa opção pode ser alterada enquanto o jogo está em execução, clicando em \"Aplicar\" abaixo; basta mover a janela de configurações para o lado e experimentar até encontrar o visual preferido para o jogo.\n\nMantenha em BILINEAR se estiver em dúvida.", - "ru_RU": "Фильтрация текстур, которая будет применяться при масштабировании.\n\nБилинейная хорошо работает для 3D-игр и является настройкой по умолчанию.\n\nСтупенчатая рекомендуется для пиксельных игр.\n\nFSR это фильтр резкости, который не рекомендуется использовать с FXAA или SMAA.\n\nЭта опция может быть изменена во время игры по нажатию кнопки \"Применить\" ниже; \nВы можете просто переместить окно настроек в сторону и поэкспериментировать, пока не подберете подходящие настройки для конкретной игры.\n\nРекомендуется использовать \"Билинейная\".", + "ru_RU": "Фильтрация текстур, которая будет применяться при масштабировании.\n\nБилинейная хорошо работает для 3D-игр и является настройкой по умолчанию.\n\nСтупенчатая рекомендуется для пиксельных игр.\n\nFSR это фильтр резкости, который не рекомендуется использовать с FXAA или SMAA.\n\nЗональная рекомендуется в случае использования разрешения больше разрешения окна. Можно использовать для достижения эффекта суперсемплига (SSAA) при даунскейле более чем в 2 раза.\n\nЭта опция может быть изменена во время игры по нажатию кнопки «Применить» ниже; \nВы можете просто переместить окно настроек в сторону и поэкспериментировать, пока не подберете подходящие настройки для конкретной игры.\n\nРекомендуется использовать «Билинейная».", "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.", @@ -22437,7 +22612,7 @@ "ko_KR": "영역", "no_NO": "Område", "pl_PL": "", - "pt_BR": "", + "pt_BR": "Área", "ru_RU": "Зональная", "sv_SE": "Yta", "th_TH": "", @@ -22488,7 +22663,7 @@ "no_NO": "Definer FSR 1,0 skarpere nivå. Høyere er skarpere.", "pl_PL": "Ustaw poziom ostrzeżenia FSR 1.0. Wyższy jest ostrzejszy.", "pt_BR": "Defina o nível de nitidez do FSR 1.0. Quanto maior, mais nítido.", - "ru_RU": "Выбор режима работы FSR 1.0. Выше - четче.", + "ru_RU": "Выбор режима работы FSR 1.0. Выше — четче.", "sv_SE": "Ställ in nivå för FSR 1.0 sharpening. Högre är skarpare.", "th_TH": "ตั้งค่าระดับความคมชัด FSR 1.0 ยิ่งสูงกว่าจะยิ่งคมชัดกว่า", "tr_TR": "", @@ -22612,7 +22787,7 @@ "ko_KR": "사용자 편집", "no_NO": "Rediger bruker", "pl_PL": "Edytuj użytkownika", - "pt_BR": "Editar usuário", + "pt_BR": "Editar Usuário", "ru_RU": "Редактирование пользователя", "sv_SE": "Redigera användare", "th_TH": "แก้ไขผู้ใช้", @@ -22662,7 +22837,7 @@ "ko_KR": "네트워크 인터페이스 :", "no_NO": "Nettverksgrensesnitt", "pl_PL": "Interfejs sieci:", - "pt_BR": "Interface de rede:", + "pt_BR": "Interface de Rede:", "ru_RU": "Сетевой интерфейс:", "sv_SE": "Nätverksgränssnitt:", "th_TH": "เชื่อมต่อเครือข่าย:", @@ -22688,7 +22863,7 @@ "no_NO": "Nettverksgrensesnittets grensesnitt brukt for LAN/LDN funksjoner.\n\ni konjuksjon med en VPN eller XLink Kai og ett spill med LAN støtte, kan bli brukt til og spoofe ett \"samme-nettverk\" tilkobling over nettet.\n\nLa være på DEFAULT om usikker.", "pl_PL": "Interfejs sieciowy używany dla funkcji LAN/LDN.\n\nw połączeniu z VPN lub XLink Kai i grą z obsługą sieci LAN, może być użyty do spoofowania połączenia z tą samą siecią przez Internet.\n\nZostaw DOMYŚLNE, jeśli nie ma pewności.", "pt_BR": "A interface de rede usada para recursos de LAN/LDN.\n\nEm conjunto com uma VPN ou XLink Kai e um jogo com suporte a LAN, pode ser usada para simular uma conexão na mesma rede pela Internet.\n\nMantenha em PADRÃO se estiver em dúvida.", - "ru_RU": "Сетевой интерфейс, используемый для функций LAN/LDN.\n\nМожет использоваться для игры через интернет в сочетании с VPN или XLink Kai и игрой с поддержкой LAN.\n\nРекомендуется использовать \"По умолчанию\".", + "ru_RU": "Сетевой интерфейс, используемый для функций LAN/LDN.\n\nМожет использоваться для игры через интернет в сочетании с VPN или XLink Kai и игрой с поддержкой LAN.\n\nРекомендуется использовать «По умолчанию».", "sv_SE": "Nätverksgränssnittet som används för LAN/LDN-funktioner.\n\nTillsammans med en VPN eller XLink Kai och ett spel med LAN-stöd så kan detta användas för att spoofa en same-network-anslutning över internet.\n\nLämna som STANDARD om du är osäker.", "th_TH": "อินเทอร์เฟซเครือข่ายที่ใช้สำหรับคุณสมบัติ LAN/LDN\n\nเมื่อใช้ร่วมกับ VPN หรือ XLink Kai และเกมที่รองรับ LAN สามารถใช้เพื่อปลอมการเชื่อมต่อเครือข่ายเดียวกันผ่านทางอินเทอร์เน็ต\n\nปล่อยให้เป็น ค่าเริ่มต้น หากคุณไม่แน่ใจ", "tr_TR": "", @@ -22762,7 +22937,7 @@ "ko_KR": "GitHub에서 변경 내역 보기", "no_NO": "Vis endringslogg på GitHub", "pl_PL": "Zobacz listę zmian na GitHubie", - "pt_BR": "Ver mudanças no GitHub", + "pt_BR": "Ver Mudanças no GitHub", "ru_RU": "Показать список изменений на GitHub", "sv_SE": "Visa ändringslogg på GitHub", "th_TH": "ดูประวัติการเปลี่ยนแปลงบน GitHub", @@ -22863,7 +23038,7 @@ "no_NO": "Endre LDN flerspillermodus.\n\nLdnMitm vil endre lokal trådløst/lokal spillfunksjonalitet i spill som skal fungere som om den var LAN, noe som tillater lokal, samme nettverk forbindelser med andre Ryujinx instanser og hacket Nintendo Switch konsoller som har installert ldn_mitm-modulen.\n\nFlerspiller krever at alle spillerne er på samme versjon (dvs. Super Smash Bros. Ultimat v13.0.1 kan ikke koble til v13.0.0).\n\nForlat DEAKTIVERT hvis usikker.", "pl_PL": "", "pt_BR": "Alterar o modo multiplayer LDN.\n\nLdnMitm modificará a funcionalidade de jogo sem fio/local nos jogos para funcionar como se fosse LAN, permitindo conexões locais, na mesma rede, com outras instâncias do Ryujinx e consoles Nintendo Switch hackeados que possuem o módulo ldn_mitm instalado.\n\nO multiplayer exige que todos os jogadores estejam na mesma versão do jogo (ex.: Super Smash Bros. Ultimate v13.0.1 não consegue se conectar à v13.0.0).\n\nDeixe DESATIVADO se estiver em dúvida.", - "ru_RU": "Меняет многопользовательский режим LDN.\n\nLdnMitm модифицирует функциональность локальной беспроводной/игры на одном устройстве в играх, позволяя играть с другими пользователями Ryujinx или взломанными консолями Nintendo Switch с установленным модулем ldn_mitm, находящимися в одной локальной сети друг с другом.\n\nМногопользовательская игра требует наличия у всех игроков одной и той же версии игры (т.е. Super Smash Bros. Ultimate v13.0.1 не может подключиться к v13.0.0).\n\nРекомендуется оставить отключенным.", + "ru_RU": "Меняет многопользовательский режим LDN.\n\nLdnMitm модифицирует функциональность локальной беспроводной/игры на одном устройстве в играх, позволяя играть с другими пользователями Ryujinx или взломанными консолями Nintendo Switch с установленным модулем ldn_mitm, находящимися в одной локальной сети друг с другом.\n\nМногопользовательская игра требует наличия у всех игроков одной и той же версии игры (т.е. Super Smash Bros. Ultimate v13.0.1 не может подключиться к v13.0.0).\n\nРекомендуется оставить выключенным.", "sv_SE": "Ändra LDN-flerspelarläge\n\nLdnMitm kommer att ändra lokal funktionalitet för trådlös/lokalt spel att fungera som om det vore ett LAN, vilket ger stöd för anslutningar med local och same-network med andra Ryujinx-instanser och hackade Nintendo Switch-konsoller som har modulen ldn_mitm installerad.\n\nFlerspelare kräver att alla spelare har samma spelversion (t.ex. Super Smash Bros. Ultimate v13.0.1 kan inte ansluta till v13.0.0).\n\nLämna INAKTIVERAD om du är osäker.", "th_TH": "เปลี่ยนโหมดผู้เล่นหลายคนของ LDN\n\nLdnMitm จะปรับเปลี่ยนฟังก์ชันการเล่นแบบไร้สาย/ภายใน จะให้เกมทำงานเหมือนกับว่าเป็น LAN ช่วยให้สามารถเชื่อมต่อภายในเครือข่ายเดียวกันกับอินสแตนซ์ Ryujinx อื่น ๆ และคอนโซล Nintendo Switch ที่ถูกแฮ็กซึ่งมีโมดูล ldn_mitm ติดตั้งอยู่\n\nผู้เล่นหลายคนต้องการให้ผู้เล่นทุกคนอยู่ในเกมเวอร์ชันเดียวกัน (เช่น Super Smash Bros. Ultimate v13.0.1 ไม่สามารถเชื่อมต่อกับ v13.0.0)\n\nปล่อยให้ปิดการใช้งานหากไม่แน่ใจ", "tr_TR": "", @@ -23012,7 +23187,7 @@ "ko_KR": "네트워크 암호 문구 :", "no_NO": "Nettverkspassord:", "pl_PL": "", - "pt_BR": "Senha de rede:", + "pt_BR": "Senha de Rede:", "ru_RU": "Cетевой пароль:", "sv_SE": "Lösenfras för nätverk:", "th_TH": "", @@ -23213,7 +23388,7 @@ "no_NO": "Ugyldig passordfrase! Må være i formatet \"Ryujinx-<8 hex tegn>\"", "pl_PL": "", "pt_BR": "Frase-senha inválida! Deve estar no formato \"Ryujinx-<8 hex chars>\"", - "ru_RU": "Неверный пароль! Пароль должен быть в формате \"Ryujinx-<8 шестнадцатеричных символов>\"", + "ru_RU": "Неверный пароль! Пароль должен быть в формате «Ryujinx-<8 шестнадцатеричных символов>»", "sv_SE": "Ogiltig lösenfras! Måste vara i formatet \"Ryujinx-<8 hextecken>\"", "th_TH": "", "tr_TR": "", @@ -23337,7 +23512,7 @@ "ko_KR": "사용자 정의 주사율", "no_NO": "Egendefinert oppdateringsfrekvens", "pl_PL": "", - "pt_BR": "Taxa de atualização personalizada", + "pt_BR": "Taxa de Atualização Personalizada", "ru_RU": "Пользовательская частота кадров", "sv_SE": "Anpassad uppdateringsfrekvens", "th_TH": "", @@ -23363,7 +23538,7 @@ "no_NO": "Emulert vertikal synkronisering. «Switch» emulerer Switchs oppdateringsfrekvens på 60 Hz. «Ubegrenset» er en ubegrenset oppdateringsfrekvens.", "pl_PL": "", "pt_BR": "Sincronização vertical emulada. 'Switch' emula a taxa de atualização de 60 Hz do Switch. 'Ilimitada' é uma taxa de atualização sem limite.", - "ru_RU": "Эмулированная вертикальная синхронизация. 'Консоль' эмулирует частоту обновления консоли, равную 60 Гц. 'Без ограничений' - неограниченная частота кадров.", + "ru_RU": "Эмулированная вертикальная синхронизация. «Консоль» эмулирует частоту обновления консоли, равную 60 Гц. «Без ограничений» — неограниченная частота кадров.", "sv_SE": "Emulerad vertikal synk. 'Switch' emulerar Switchens uppdateringsfrekvens på 60Hz. 'Obunden' är en obegränsad uppdateringsfrekvens.", "th_TH": "", "tr_TR": "", @@ -23388,7 +23563,7 @@ "no_NO": "Emulert vertikal synkronisering. «Switch» emulerer Switchs oppdateringsfrekvens på 60 Hz. «Ubegrenset» er en ubegrenset oppdateringsfrekvens. «Egendefinert» emulerer den angitte egendefinerte oppdateringsfrekvensen.", "pl_PL": "", "pt_BR": "Sincronização Vertical Emulada. 'Switch' emula a taxa de atualização de 60 Hz do Switch. 'Ilimitada' é uma taxa de atualização sem limite. 'Taxa de atualização personalizada' emula a taxa de atualização personalizada especificada.", - "ru_RU": "Эмулированная вертикальная синхронизация. 'Консоль' эмулирует частоту обновления консоли, равную 60 Гц. 'Без ограничений' - неограниченная частота кадров. 'Пользовательска частота кадров' эмулирует выбранную пользователем частоту кадров.", + "ru_RU": "Эмулированная вертикальная синхронизация. «Консоль» эмулирует частоту обновления консоли, равную 60 Гц. «Без ограничений» — неограниченная частота кадров. «Пользовательска частота кадров» эмулирует выбранную пользователем частоту кадров.", "sv_SE": "Emulerad vertikal synk. 'Switch' emulerar Switchens uppdateringsfrekvens på 60Hz. 'Obunden' är en obegränsad uppdateringsfrekvens. 'Anpassad uppdateringsfrekvens' emulerar den angivna anpassade uppdateringsfrekvensen.", "th_TH": "", "tr_TR": "", @@ -23437,7 +23612,7 @@ "ko_KR": "사용자 정의 주사율 목표 값입니다.", "no_NO": "Den egendefinerte målverdien for oppdateringsfrekvens.", "pl_PL": "", - "pt_BR": "O valor alvo da taxa de atualização personalizada.", + "pt_BR": "O valor de meta da taxa de atualização personalizada.", "ru_RU": "Заданное значение частоты кадров", "sv_SE": "Målvärde för anpassad uppdateringsfrekvens.", "th_TH": "", @@ -23487,7 +23662,7 @@ "ko_KR": "사용자 정의 주사율 % :", "no_NO": "Egendefinert oppdateringsfrekvens %:", "pl_PL": "", - "pt_BR": "Taxa de atualização personalizada %:", + "pt_BR": "Taxa de Atualização Personalizada %:", "ru_RU": "Пользовательская частота кадров %:", "sv_SE": "Anpassad uppdateringsfrekvens %:", "th_TH": "", @@ -23512,7 +23687,7 @@ "ko_KR": "사용자 정의 주사율 값 :", "no_NO": "Egendefinert verdi for oppdateringsfrekvens:", "pl_PL": "", - "pt_BR": "Valor da taxa de atualização personalizada:", + "pt_BR": "Valor da Taxa de Atualização Personalizada:", "ru_RU": "Значение пользовательской частоты кадров:", "sv_SE": "Värde för anpassad uppdateringsfrekvens:", "th_TH": "", @@ -23542,7 +23717,7 @@ "sv_SE": "Intervall", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Інтервал", "zh_CN": "间隔", "zh_TW": "間隔" } @@ -23562,7 +23737,7 @@ "ko_KR": "수직 동기화 모드 전환 :", "no_NO": "Veksle mellom VSync-modus:", "pl_PL": "", - "pt_BR": "Alternar modo VSync:", + "pt_BR": "Alternar Modo VSync:", "ru_RU": "Выбрать режим вертикальной синхронизации:", "sv_SE": "Växla VSync-läge:", "th_TH": "", @@ -23587,7 +23762,7 @@ "ko_KR": "사용자 정의 주사율 증가", "no_NO": "Øk den egendefinerte oppdateringsfrekvensen", "pl_PL": "", - "pt_BR": "Aumentar taxa de atualização personalizada", + "pt_BR": "Aumentar Taxa de Atualização:", "ru_RU": "Повышение пользовательской частоты кадров", "sv_SE": "Höj anpassad uppdateringsfrekvens", "th_TH": "", @@ -23612,7 +23787,7 @@ "ko_KR": "사용자 정의 주사율 감소", "no_NO": "Lavere tilpasset oppdateringsfrekvens", "pl_PL": "", - "pt_BR": "Reduzir Taxa de atualização personalizada:", + "pt_BR": "Reduzir Taxa de Atualização:", "ru_RU": "Понижение пользовательской частоты кадров", "sv_SE": "Sänk anpassad uppdateringsfrekvens", "th_TH": "", @@ -23638,11 +23813,11 @@ "no_NO": "Sist oppdatert: {0}", "pl_PL": "", "pt_BR": "Última atualização: {0}", - "ru_RU": "", + "ru_RU": "Последнее обновление: {0}", "sv_SE": "Senast uppdaterad: {0}", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Останнє оновлення: {0}", "zh_CN": "最后更新于: {0}", "zh_TW": "上次更新時間: {0}" } @@ -23663,11 +23838,11 @@ "no_NO": "Denne kompatibilitetslisten kan inneholde oppføringer som er tomme for data.\nVær ikke imot å teste spill i statusen «Ingame».", "pl_PL": "", "pt_BR": "Esta lista de compatibilidade pode estar desatualizada.\nNão se oponha a testar os jogos", - "ru_RU": "", + "ru_RU": "В списке совместимости могут содержаться устаревшие записи.\nНе стестняйтесь тестировать игр в статусе «Запускается»", "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)」狀態的遊戲並回報以更新紀錄。" } @@ -23688,11 +23863,11 @@ "no_NO": "Søk i kompatibilitetsoppføringer...", "pl_PL": "", "pt_BR": "Pesquisa de compatibilidade", - "ru_RU": "", + "ru_RU": "Поиск записей о совместимости...", "sv_SE": "Sök i kompatibilitetsposter...", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Перевірити сумісність гри...", "zh_CN": "正在搜索兼容性条目...", "zh_TW": "搜尋相容性列表紀錄..." } @@ -23713,11 +23888,11 @@ "no_NO": "Åpne kompatibilitetslisten", "pl_PL": "", "pt_BR": "Lista de Compatibilidade", - "ru_RU": "", + "ru_RU": "Открыть список совместимости", "sv_SE": "Öppna kompatibilitetslistan", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Відкрити Список Сумісності", "zh_CN": "打开兼容性列表", "zh_TW": "開啟相容性列表" } @@ -23738,11 +23913,11 @@ "no_NO": "Vis bare eide spill", "pl_PL": "", "pt_BR": "Mostrar apenas jogos disponíveis", - "ru_RU": "", + "ru_RU": "Показывать только свои игры", "sv_SE": "Visa endast ägda spel", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Показувати лише ігри в наявності", "zh_CN": "仅显示拥有的游戏", "zh_TW": "只顯示已擁有的遊戲" } @@ -23763,11 +23938,11 @@ "no_NO": "Spillbar", "pl_PL": "", "pt_BR": "Jogável", - "ru_RU": "", + "ru_RU": "Играбельно", "sv_SE": "Spelbart", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Оптимально", "zh_CN": "可游玩", "zh_TW": "可暢順遊玩" } @@ -23788,11 +23963,11 @@ "no_NO": "", "pl_PL": "", "pt_BR": "No jogo", - "ru_RU": "", + "ru_RU": "Запускается", "sv_SE": "Spelproblem", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "З недоліками", "zh_CN": "进入游戏", "zh_TW": "大致可遊玩" } @@ -23813,11 +23988,11 @@ "no_NO": "Menyer", "pl_PL": "", "pt_BR": "Menu", - "ru_RU": "", + "ru_RU": "Меню", "sv_SE": "Menyer", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Меню", "zh_CN": "菜单", "zh_TW": "只開啟至遊戲開始功能表" } @@ -23838,11 +24013,11 @@ "no_NO": "Starter", "pl_PL": "", "pt_BR": "Inicializa", - "ru_RU": "", + "ru_RU": "Стартует", "sv_SE": "Startar", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Запускається", "zh_CN": "启动", "zh_TW": "只能啟動" } @@ -23863,11 +24038,11 @@ "no_NO": "Ingenting", "pl_PL": "", "pt_BR": "Nada", - "ru_RU": "", + "ru_RU": "Ничего", "sv_SE": "Ingenting", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Не працює", "zh_CN": "什么都没有", "zh_TW": "無法啟動" } @@ -23888,11 +24063,11 @@ "no_NO": "Starter opp og spiller uten krasj eller GPU-feil av noe slag, og med en hastighet som er rask nok til å ha rimelig glede av på en gjennomsnittlig PC.", "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": "", + "ru_RU": "Запускается и работает без любого рода сбоев или графисечких ошибок и на скорости, достаточной для работы на обычном ПК.", + "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": "" } @@ -23913,11 +24088,11 @@ "no_NO": "Starter og går i gang i spillet, men lider av ett eller flere av følgende: krasjer, fastlåser, GPU-feil, distraherende dårlig lyd eller er rett og slett for tregt. Spillet kan fortsatt spilles helt til ende, men ikke slik det er ment å spilles.", "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": "", - "sv_SE": "", + "ru_RU": "Запускается и работает, но возникает одна или несколько из следующих проблем: сбои, взаимоблокировки, ошибки GPU, отвлекающие звуки или просто слишком медленная работа. Возможно, игру всё же удастся пройти до конца, но не так, как она задумана.", + "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": "" } @@ -23938,11 +24113,11 @@ "no_NO": "Starter opp og går forbi tittelskjermen, men kommer ikke inn i hovedspillet.", "pl_PL": "", "pt_BR": "Inicializa e passa da tela de título, mas não entra no jogo principal.", - "ru_RU": "", - "sv_SE": "", + "ru_RU": "Загружается титульный экран и можно перейти дальше, но сама игра не работает.", + "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": "" } @@ -23963,11 +24138,11 @@ "no_NO": "Starter, men kommer ikke lenger enn til tittelskjermen.", "pl_PL": "", "pt_BR": "Inizializa, mas não passa da tela de título.", - "ru_RU": "", - "sv_SE": "", + "ru_RU": "Загружается, но не проходит дальше титульного экрана.", + "sv_SE": "Startar upp men tar sig inte förbi titelskärmen.", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Запускається, але не відображає навіть початкового екрану.", "zh_CN": "可以启动但是无法通过标题画面。", "zh_TW": "" } @@ -23988,12 +24163,62 @@ "no_NO": "Starter ikke opp eller viser ingen tegn til aktivitet.", "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": "无法启动或显示无任何动静。", + "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": "" } }, @@ -24012,12 +24237,12 @@ "ko_KR": "추출할 DLC 선택", "no_NO": "Velg en DLC og hente ut", "pl_PL": "", - "pt_BR": "Selecione um DLC para extrair", - "ru_RU": "", + "pt_BR": "Selecione um DLC para Extrair", + "ru_RU": "Выберите DLC для извлечения", "sv_SE": "Välj en DLC att extrahera", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Оберіть DLC які бажаєте вилучити", "zh_CN": "选择一个要解压的 DLC", "zh_TW": "" } @@ -24037,12 +24262,12 @@ "ko_KR": "", "no_NO": "Rikt nærværsbilde", "pl_PL": "", - "pt_BR": "", - "ru_RU": "", - "sv_SE": "", + "pt_BR": "Imagem da Presença do Discord", + "ru_RU": "Изображение для статуса активности", + "sv_SE": "Bild för Rich Presence", "th_TH": "", "tr_TR": "", - "uk_UA": "", + "uk_UA": "Зображення картки активності Discord", "zh_CN": "Rich Presence 图像", "zh_TW": "" } @@ -24062,12 +24287,12 @@ "ko_KR": "", "no_NO": "Dynamisk og rik tilstedeværelse", "pl_PL": "", - "pt_BR": "", - "ru_RU": "", - "sv_SE": "", + "pt_BR": "Presença Dinâmica do Discord", + "ru_RU": "Динамический статус активности", + "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..86e5ee310 100644 --- a/src/Ryujinx/Common/ApplicationHelper.cs +++ b/src/Ryujinx/Common/ApplicationHelper.cs @@ -13,7 +13,7 @@ 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; diff --git a/src/Ryujinx/Common/LocaleManager.cs b/src/Ryujinx/Common/LocaleManager.cs index 4c86a6177..f60cff49b 100644 --- a/src/Ryujinx/Common/LocaleManager.cs +++ b/src/Ryujinx/Common/LocaleManager.cs @@ -54,6 +54,7 @@ namespace Ryujinx.Ava.Common.Locale SetDynamicValues(LocaleKeys.RyujinxInfo, RyujinxApp.FullAppName); SetDynamicValues(LocaleKeys.RyujinxConfirm, RyujinxApp.FullAppName); SetDynamicValues(LocaleKeys.RyujinxUpdater, RyujinxApp.FullAppName); + SetDynamicValues(LocaleKeys.RyujinxRebooter, RyujinxApp.FullAppName); } public string this[LocaleKeys key] diff --git a/src/Ryujinx/DiscordIntegrationModule.cs b/src/Ryujinx/DiscordIntegrationModule.cs index 47fc8ad69..8d232b4fb 100644 --- a/src/Ryujinx/DiscordIntegrationModule.cs +++ b/src/Ryujinx/DiscordIntegrationModule.cs @@ -24,7 +24,7 @@ namespace Ryujinx.Ava private static readonly string _description = ReleaseInformation.IsValid - ? $"{VersionString} {ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelSourceRepo}@{ReleaseInformation.BuildGitHash}" + ? $"{VersionString} {ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelSourceRepo}" : "dev build"; private const string ApplicationId = "1293250299716173864"; @@ -56,6 +56,7 @@ namespace Ryujinx.Ava ConfigurationState.Instance.EnableDiscordIntegration.Event += Update; TitleIDs.CurrentApplication.Event += (_, e) => Use(e.NewValue); HorizonStatic.PlayReport += HandlePlayReport; + PlayReports.Initialize(); } private static void Update(object sender, ReactiveEventArgs evnt) diff --git a/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs b/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs index aa8238fc6..d6a2ed789 100644 --- a/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs +++ b/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs @@ -12,7 +12,6 @@ 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; @@ -310,57 +309,45 @@ 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, - _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, + !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 + ) + ); } } diff --git a/src/Ryujinx/Headless/HeadlessRyujinx.cs b/src/Ryujinx/Headless/HeadlessRyujinx.cs index fafcbf01e..9a69c56dd 100644 --- a/src/Ryujinx/Headless/HeadlessRyujinx.cs +++ b/src/Ryujinx/Headless/HeadlessRyujinx.cs @@ -289,7 +289,8 @@ namespace Ryujinx.Headless DriverUtilities.InitDriverConfig(option.BackendThreading == BackendThreading.Off); - if (_inputConfiguration.OfType().Any(ic => ic.Led.UseRainbow)) + if (_inputConfiguration.OfType() + .Any(ic => ic?.Led?.UseRainbow ?? false)) Rainbow.Enable(); while (true) @@ -357,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 a57863d5d..6658ba3ad 100644 --- a/src/Ryujinx/Headless/Options.cs +++ b/src/Ryujinx/Headless/Options.cs @@ -148,7 +148,7 @@ namespace Ryujinx.Headless IgnoreMissingServices = configurationState.System.IgnoreMissingServices; if (NeedsOverride(nameof(IgnoreControllerApplet))) - IgnoreControllerApplet = configurationState.System.IgnoreApplet; + IgnoreControllerApplet = configurationState.System.IgnoreControllerApplet; return; 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 831e4294c..edd46a7db 100644 --- a/src/Ryujinx/Program.cs +++ b/src/Ryujinx/Program.cs @@ -32,8 +32,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); @@ -47,6 +49,7 @@ namespace Ryujinx.Ava if (OperatingSystem.IsWindows() && !OperatingSystem.IsWindowsVersionAtLeast(10, 0, 19041)) { _ = MessageBoxA(nint.Zero, "You are running an outdated version of Windows.\n\nRyujinx supports Windows 10 version 20H1 and newer.\n", $"Ryujinx {Version}", MbIconwarning); + return 0; } PreviewerDetached = true; @@ -155,11 +158,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)) { @@ -203,7 +233,6 @@ namespace Ryujinx.Ava { "opengl" => GraphicsBackend.OpenGl, "vulkan" => GraphicsBackend.Vulkan, - "metal" => GraphicsBackend.Metal, _ => ConfigurationState.Instance.Graphics.GraphicsBackend }; @@ -217,6 +246,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; @@ -232,6 +266,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 Ryujinx.HLE.HOS.SystemState.RegionCode result)) + { + ConfigurationState.Instance.System.Region.Value = (Utilities.Configuration.System.Region)result; + } + + //Check if language was overridden. + if (CommandLineState.OverrideSystemLanguage is not null) + if (Enum.TryParse(CommandLineState.OverrideSystemLanguage, true, out Ryujinx.HLE.HOS.SystemState.SystemLanguage result)) + { + ConfigurationState.Instance.System.Language.Value = (Utilities.Configuration.System.Language)result; + } // Check if hardware-acceleration was overridden. if (CommandLineState.OverrideHardwareAcceleration != null) diff --git a/src/Ryujinx/Rebooter.cs b/src/Ryujinx/Rebooter.cs new file mode 100644 index 000000000..8c7755a4c --- /dev/null +++ b/src/Ryujinx/Rebooter.cs @@ -0,0 +1,76 @@ +using FluentAvalonia.UI.Controls; +using Ryujinx.Ava.Common.Locale; +using Ryujinx.Ava.UI.ViewModels; +using Ryujinx.Ava.Utilities; +using SkiaSharp; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Threading.Tasks; + +namespace Ryujinx.Ava +{ + 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/Ryujinx.csproj b/src/Ryujinx/Ryujinx.csproj index 174aadeb0..698b392ee 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 @@ + @@ -127,6 +125,7 @@ + @@ -169,8 +168,4 @@ - - - - diff --git a/src/Ryujinx/UI/Applet/AvaHostUIHandler.cs b/src/Ryujinx/UI/Applet/AvaHostUIHandler.cs index c03c4c45f..a8a6f24c6 100644 --- a/src/Ryujinx/UI/Applet/AvaHostUIHandler.cs +++ b/src/Ryujinx/UI/Applet/AvaHostUIHandler.cs @@ -41,7 +41,7 @@ namespace Ryujinx.Ava.UI.Applet bool okPressed = false; - if (ConfigurationState.Instance.System.IgnoreApplet) + if (ConfigurationState.Instance.System.IgnoreControllerApplet) return false; Dispatcher.UIThread.InvokeAsync(async () => @@ -95,7 +95,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}}" /> + + cacheFiles = new(); + List cacheFiles = []; if (mainDir.Exists) { @@ -200,7 +205,7 @@ namespace Ryujinx.Ava.UI.Controls if (backupDir.Exists) { cacheFiles.AddRange(backupDir.EnumerateFiles("*.cache")); - cacheFiles.AddRange(mainDir.EnumerateFiles("*.info")); + cacheFiles.AddRange(backupDir.EnumerateFiles("*.info")); } if (cacheFiles.Count > 0) @@ -386,13 +391,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); } - + 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/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/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/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/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/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/RendererHost.cs b/src/Ryujinx/UI/Renderer/RendererHost.cs index f755b6d70..7352dbdb5 100644 --- a/src/Ryujinx/UI/Renderer/RendererHost.cs +++ b/src/Ryujinx/UI/Renderer/RendererHost.cs @@ -2,9 +2,7 @@ using Avalonia.Controls; using Avalonia.Media; using Ryujinx.Ava.Utilities.Configuration; -using Ryujinx.Common; 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/UI/ViewModels/ApplicationDataViewModel.cs b/src/Ryujinx/UI/ViewModels/ApplicationDataViewModel.cs index 592d5f81a..33c75bc62 100644 --- a/src/Ryujinx/UI/ViewModels/ApplicationDataViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/ApplicationDataViewModel.cs @@ -1,6 +1,7 @@ using Gommon; using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Utilities.AppLibrary; +using Ryujinx.Ava.Utilities.PlayReport; namespace Ryujinx.Ava.UI.ViewModels { @@ -10,6 +11,11 @@ namespace Ryujinx.Ava.UI.ViewModels public ApplicationDataViewModel(ApplicationData appData) => AppData = appData; + public string DynamicRichPresenceDescription => + AppData.HasDynamicRichPresenceSupport + ? AppData.RichPresenceSpec.Value.Description + : GameSpec.DefaultDescription; + public string FormattedVersion => LocaleManager.Instance[LocaleKeys.GameListHeaderVersion].Format(AppData.Version); public string FormattedDeveloper => LocaleManager.Instance[LocaleKeys.GameListHeaderDeveloper].Format(AppData.Developer); public string FormattedFileExtension => LocaleManager.Instance[LocaleKeys.GameListHeaderFileExtension].Format(AppData.FileExtension); diff --git a/src/Ryujinx/UI/ViewModels/Input/ControllerInputViewModel.cs b/src/Ryujinx/UI/ViewModels/Input/ControllerInputViewModel.cs index 2b644cffa..96da58b5d 100644 --- a/src/Ryujinx/UI/ViewModels/Input/ControllerInputViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/Input/ControllerInputViewModel.cs @@ -1,5 +1,9 @@ using Avalonia.Svg.Skia; using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using FluentAvalonia.UI.Controls; +using Ryujinx.Ava.Input; +using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Models.Input; using Ryujinx.Ava.UI.Views.Input; using Ryujinx.Common.Utilities; @@ -10,8 +14,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 +63,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 5b7bcfd32..b324d39e8 100644 --- a/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs @@ -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); } } @@ -893,6 +908,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/MainWindowViewModel.cs b/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs index bbfe80570..2c42a1a1d 100644 --- a/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs @@ -141,7 +141,8 @@ namespace Ryujinx.Ava.UI.ViewModels // 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")!); + new(Assembly.GetAssembly(typeof(MainWindowViewModel))! + .GetManifestResourceStream("Ryujinx.Assets.UIImages.Logo_Ryujinx_AntiAlias.png")!); public MainWindow Window { get; init; } @@ -355,6 +356,11 @@ namespace Ryujinx.Ava.UI.ViewModels _ => null, }; } + set + { + ListSelectedApplication = value; + GridSelectedApplication = value; + } } public bool HasCompatibilityEntry => SelectedApplication.HasPlayabilityInfo; @@ -793,7 +799,7 @@ namespace Ryujinx.Ava.UI.ViewModels return false; } - private async Task HandleFirmwareInstallation(string filename) + public async Task HandleFirmwareInstallation(string filename) { try { @@ -1084,7 +1090,7 @@ namespace Ryujinx.Ava.UI.ViewModels _rendererWaitEvent.WaitOne(); AppHost?.Start(); - + AppHost?.DisposeContext(); } @@ -1347,6 +1353,25 @@ namespace Ryujinx.Ava.UI.ViewModels OpenHelper.OpenFolder(AppDataManager.BaseDirPath); } + public void OpenScreenshotsFolder() + { + string screenshotsDir = Path.Combine(AppDataManager.BaseDirPath, "screenshots"); + + try + { + if (!Directory.Exists(screenshotsDir)) + Directory.CreateDirectory(screenshotsDir); + } + catch (Exception ex) + { + Logger.Error?.Print(LogClass.Application, $"Failed to create directory at path {screenshotsDir}. Error : {ex.GetType().Name}", "Screenshot"); + + return; + } + + OpenHelper.OpenFolder(screenshotsDir); + } + public void OpenLogsFolder() { string logPath = AppDataManager.GetOrCreateLogsDir(); @@ -1531,8 +1556,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( @@ -1548,14 +1615,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, @@ -1593,6 +1660,7 @@ namespace Ryujinx.Ava.UI.ViewModels Thread gameThread = new(InitializeGame) { Name = "GUI.WindowThread" }; gameThread.Start(); + } public void SwitchToRenderer(bool startFullscreen) => @@ -1679,7 +1747,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) { diff --git a/src/Ryujinx/UI/ViewModels/SettingsHacksViewModel.cs b/src/Ryujinx/UI/ViewModels/SettingsHacksViewModel.cs index 5096a716d..167b8a857 100644 --- a/src/Ryujinx/UI/ViewModels/SettingsHacksViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/SettingsHacksViewModel.cs @@ -16,21 +16,6 @@ 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)); - } - } public static string Xc2MenuFixTooltip { get; } = Lambda.String(sb => { @@ -44,13 +29,5 @@ 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 => - { - sb.AppendLine("This hack applies the delay you specify every time shaders are attempted to be translated.") - .AppendLine(); - - sb.Append("Configurable via slider, only when this option is enabled."); - }); } } diff --git a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs index 1b1f8a2a5..d0a6c6d8a 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; @@ -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; @@ -49,8 +51,7 @@ namespace Ryujinx.Ava.UI.ViewModels private int _graphicsBackendMultithreadingIndex; private float _volume; [ObservableProperty] private bool _isVulkanAvailable = true; - [ObservableProperty] private bool _gameDirectoryChanged; - [ObservableProperty] private bool _autoloadDirectoryChanged; + [ObservableProperty] private bool _gameListNeedsRefresh; private readonly List _gpuIds = []; private int _graphicsBackendIndex; private int _scalingFilter; @@ -69,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; @@ -336,7 +350,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; @@ -349,7 +363,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 = []; @@ -364,7 +422,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); } @@ -527,7 +587,7 @@ namespace Ryujinx.Ava.UI.ViewModels EnableFsIntegrityChecks = config.System.EnableFsIntegrityChecks; DramSize = config.System.DramSize; IgnoreMissingServices = config.System.IgnoreMissingServices; - IgnoreApplet = config.System.IgnoreApplet; + IgnoreApplet = config.System.IgnoreControllerApplet; // CPU EnablePptc = config.System.EnablePtc; @@ -593,16 +653,8 @@ namespace Ryujinx.Ava.UI.ViewModels config.HideCursor.Value = (HideCursorMode)HideCursor; config.UpdateCheckerType.Value = (UpdaterType)UpdateCheckerType; config.FocusLostActionType.Value = (FocusLostType)FocusLostActionType; - - if (GameDirectoryChanged) - { - config.UI.GameDirs.Value = [..GameDirectories]; - } - - if (AutoloadDirectoryChanged) - { - config.UI.AutoloadDirs.Value = [..AutoloadDirectories]; - } + config.UI.GameDirs.Value = [.. GameDirectories]; + config.UI.AutoloadDirs.Value = [.. AutoloadDirectories]; config.UI.BaseStyle.Value = BaseStyleIndex switch { @@ -623,8 +675,11 @@ namespace Ryujinx.Ava.UI.ViewModels // System config.System.Region.Value = (Region)Region; - config.System.Language.Value = (Language)Language; + if (config.System.Language.Value != (Language)Language) + GameListNeedsRefresh = true; + + config.System.Language.Value = (Language)Language; if (_validTzRegions.Contains(TimeZone)) { config.System.TimeZone.Value = TimeZone; @@ -635,7 +690,7 @@ namespace Ryujinx.Ava.UI.ViewModels config.System.EnableFsIntegrityChecks.Value = EnableFsIntegrityChecks; config.System.DramSize.Value = DramSize; config.System.IgnoreMissingServices.Value = IgnoreMissingServices; - config.System.IgnoreApplet.Value = IgnoreApplet; + config.System.IgnoreControllerApplet.Value = IgnoreApplet; // CPU config.System.EnablePtc.Value = EnablePptc; @@ -702,11 +757,9 @@ 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.ToFileFormat().SaveConfig(Program.ConfigurationPath); @@ -715,13 +768,16 @@ namespace Ryujinx.Ava.UI.ViewModels SaveSettingsEvent?.Invoke(); - GameDirectoryChanged = false; - AutoloadDirectoryChanged = false; + GameListNeedsRefresh = false; } 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() @@ -729,6 +785,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/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/Views/Input/ControllerInputView.axaml b/src/Ryujinx/UI/Views/Input/ControllerInputView.axaml index 49c2cfd4c..555ded9fc 100644 --- a/src/Ryujinx/UI/Views/Input/ControllerInputView.axaml +++ b/src/Ryujinx/UI/Views/Input/ControllerInputView.axaml @@ -34,12 +34,7 @@ - - - - - + MinHeight="450" ColumnDefinitions="Auto,*,Auto"> - - - - - - - - + HorizontalAlignment="Stretch" ColumnDefinitions="*,*" RowDefinitions="*,*"> - + + + + + + + + + + + + + + + + + + + + + + + + + @@ -345,8 +414,8 @@ Minimum="0" Value="{Binding Config.TriggerThreshold, Mode=TwoWay}" /> + Width="25" + Text="{Binding Config.TriggerThreshold, StringFormat=\{0:0.00\}}" /> @@ -438,11 +507,7 @@ CornerRadius="5" VerticalAlignment="Bottom" HorizontalAlignment="Stretch"> - - - - - + - - - - - + - - - - - + - - - - - - - - + HorizontalAlignment="Stretch" ColumnDefinitions="*,*" RowDefinitions="*,*"> { private ButtonKeyAssigner _currentAssigner; @@ -217,20 +218,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 e830412c1..22fd369d9 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,106 +61,103 @@ 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(); + 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; } } diff --git a/src/Ryujinx/UI/Views/Input/LedInputView.axaml.cs b/src/Ryujinx/UI/Views/Input/LedInputView.axaml.cs index ce28f6c1c..3252ee7e2 100644 --- a/src/Ryujinx/UI/Views/Input/LedInputView.axaml.cs +++ b/src/Ryujinx/UI/Views/Input/LedInputView.axaml.cs @@ -2,19 +2,18 @@ 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 +28,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 +54,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 +19,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 +32,6 @@ namespace Ryujinx.Ava.UI.Views.Input }; InitializeComponent(); - DataContext = _viewModel; } public static async Task Show(ControllerInputViewModel viewModel) @@ -48,17 +46,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 +19,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 +41,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 cacb4b130..1da91c388 100644 --- a/src/Ryujinx/UI/Views/Main/MainMenuBarView.axaml +++ b/src/Ryujinx/UI/Views/Main/MainMenuBarView.axaml @@ -66,6 +66,10 @@ Command="{Binding OpenRyujinxFolder}" Header="{ext:Locale MenuBarFileOpenEmuFolder}" ToolTip.Tip="{ext:Locale OpenRyujinxFolderTooltip}" /> + { public MainWindow Window { get; private set; } - public MainWindowViewModel ViewModel { get; private set; } public MainMenuBarView() { @@ -72,7 +73,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)) } ); @@ -107,7 +108,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; @@ -130,9 +131,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(); @@ -158,11 +176,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(); } diff --git a/src/Ryujinx/UI/Views/Main/MainStatusBarView.axaml.cs b/src/Ryujinx/UI/Views/Main/MainStatusBarView.axaml.cs index 78747013c..5a023910c 100644 --- a/src/Ryujinx/UI/Views/Main/MainStatusBarView.axaml.cs +++ b/src/Ryujinx/UI/Views/Main/MainStatusBarView.axaml.cs @@ -4,6 +4,8 @@ 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.Common; @@ -13,7 +15,7 @@ using System; namespace Ryujinx.Ava.UI.Views.Main { - public partial class MainStatusBarView : UserControl + public partial class MainStatusBarView : RyujinxControl { public MainWindow Window; @@ -29,7 +31,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/ApplicationDataView.axaml b/src/Ryujinx/UI/Views/Misc/ApplicationDataView.axaml similarity index 94% rename from src/Ryujinx/UI/Controls/ApplicationDataView.axaml rename to src/Ryujinx/UI/Views/Misc/ApplicationDataView.axaml index aee8f7b36..a36b637ec 100644 --- a/src/Ryujinx/UI/Controls/ApplicationDataView.axaml +++ b/src/Ryujinx/UI/Views/Misc/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.Misc.ApplicationDataView" x:DataType="viewModels:ApplicationDataViewModel"> - - + + + TextWrapping="Wrap"> - + { public static async Task Show(ApplicationData appData) { @@ -25,20 +27,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() 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 84% rename from src/Ryujinx/UI/Controls/ApplicationGridView.axaml.cs rename to src/Ryujinx/UI/Views/Misc/ApplicationGridView.axaml.cs index 22aefc3b9..bb7d62c8f 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.UI.ViewModels; using Ryujinx.Ava.Utilities.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,9 +33,6 @@ 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; @@ -43,16 +41,13 @@ namespace Ryujinx.Ava.UI.Controls 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/Controls/DlcSelectView.axaml b/src/Ryujinx/UI/Views/Misc/DlcSelectView.axaml similarity index 98% rename from src/Ryujinx/UI/Controls/DlcSelectView.axaml rename to src/Ryujinx/UI/Views/Misc/DlcSelectView.axaml index 790c6dd3b..f44bc3261 100644 --- a/src/Ryujinx/UI/Controls/DlcSelectView.axaml +++ b/src/Ryujinx/UI/Views/Misc/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.Misc.DlcSelectView" x:DataType="viewModels:DlcSelectViewModel"> { public DlcSelectView() { @@ -28,20 +29,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/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..4cf96366a 100644 --- a/src/Ryujinx/UI/Views/Settings/SettingsHacksView.axaml +++ b/src/Ryujinx/UI/Views/Settings/SettingsHacksView.axaml @@ -43,39 +43,6 @@ Text="Xenoblade Chronicles 2 Menu Softlock Fix" /> - - - - - - - - - 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/SettingsSystemView.axaml b/src/Ryujinx/UI/Views/Settings/SettingsSystemView.axaml index aa7144cf1..dd6858ee6 100644 --- a/src/Ryujinx/UI/Views/Settings/SettingsSystemView.axaml +++ b/src/Ryujinx/UI/Views/Settings/SettingsSystemView.axaml @@ -133,12 +133,6 @@ - - - - - - + + - + + - + ToolTip.Tip="{ext:Locale IgnoreControllerAppletTooltip}"> + - + + + - - + + + + + - - + + + + + - - + + + + + - + @@ -64,7 +86,11 @@ - + @@ -81,8 +107,11 @@ + - + @@ -100,7 +129,11 @@ - + + - + + + + - - - - - - + - + + + + - - - - - - + (GameDirsList.SelectedItems.Cast())) { ViewModel.GameDirectories.Remove(path); - ViewModel.GameDirectoryChanged = true; + ViewModel.GameListNeedsRefresh = true; } if (GameDirsList.ItemCount > 0) @@ -81,7 +75,7 @@ namespace Ryujinx.Ava.UI.Views.Settings foreach (string path in new List(AutoloadDirsList.SelectedItems.Cast())) { ViewModel.AutoloadDirectories.Remove(path); - ViewModel.AutoloadDirectoryChanged = true; + ViewModel.GameListNeedsRefresh = true; } if (AutoloadDirsList.ItemCount > 0) 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 +41,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 +50,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 +69,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 +86,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 +112,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 +148,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,*">